开发者

Why would you declare an Interface and then instantiate an object with it in Java?

A friend and I are studying Java. We were looking at interfaces today and we got into a bit of an discussion about how interfaces are used.

The example 开发者_如何学运维code my friend showed me contained this:

IVehicle modeOfTransport1 = new Car();
IVehicle modeOfTransport2 = new Bike();

Where IVehicle is an interface that's implemented in both the car and bike classes. When defining a method that accepts IVehicle as a parameter you can use the interface methods, and when you run the code the above objects work as normal. However, this works perfectly fine when declaring the car and bike as you normally would like this:

Car modeOfTransport1 = new Car();
Bike modeOfTransport2 = new Bike();

So, my question is - why would you use the former method over the latter when declaring and instantiating the modeOfTransport objects? Does it matter?


There is a big plus on declaring them using the interface, which is what is known as "coding to an interface" instead of "coding to an implementation" which is a big Object Oriented Design (OOD) principle, this way you can declare a method like this:

public void (IVehicle myVehicle)

and this will accept any object that implements that interface, then at runtime it will call the implementation like this:

public void (IVehicle myVehicle)
{
    myVehicle.run() //This calls the implementation for that particular vehicle.
}

To answer the original question, why would you use one over the other there are several reasons:

1) Declaring them using an interface, means you can later substitute that value with any other concrete class that implements that interface, instead of being locked into that particular concrete class

2) You can take full advantage of polymorphism by declaring them using an interface, because each implementation can call the correct method at runtime.

3) You follow the OOD principle of code to an interface


It doesn't matter there.

Where it really matters is in other interfaces that need to operate on IVehicle. If they accept parameters and return values as IVehicle, then the code will be more easily extendible.

As you noted, either of these objects can be passed to a method that accepts IVehicle as a parameter.

If you had subsequent code that used Car or Bike specific operations that were used, then it would be advantageous to declare them as Car or Bike. The Car and Bike specific operations would be available for each of the relevant objects, and both would be usable (i.e. could be passed) as IVehicle.


You're really asking: what reference type should I use?

Generally you want to use as general a reference type as possible that still gives you access to the behavior that you need. This means any of the interfaces or parent classes of your concrete type, rather than the concrete type itself. Of course, don't take this point too far -- for example, you certainly don't want to declare everything as an Object!

Consider these options:

Set<String> values1 = new TreeSet<String>();
TreeSet<String> values2 = new TreeSet<String>();
SortedSet<String> values3 = new TreeSet<String>();

All three are valid, but generally the first option of values1 is better because you will only be able to access the behavior of the Set interface, so later you can swap in another implementation quite easily:

Set<String> values1 = new HashSet<String>();

Beware of using the second option values2. It allows you to use specific behavior of the TreeSet implementation in such a way that swapping in a different implementation of Set becomes more difficult. This is fine as long as that's your goal. So, in your example, use a Car or Bike reference only when you need access to something that's not in the IVehicle interface. Be aware though that the following would not work:

TreeSet<String> values2 = new HashSet<String>(); // does not compile!

Still there are times when you need access to the methods that are not in the most general type. This is illustrated in the third option values3 -- the reference is more specific than Set, which allows you to rely on the behavior of SortedSet later.

TreeSet<String> values3 = new ConcurrentSkipListSet<String>();

The question about reference types applies not only where variables are declared, but also in methods where you have to specify the type of each parameter. Fortunately the "use as general a reference type as possible" rule of thumb applies to method parameters, too.


Program to an interface rather than an implementation.

When you program to an interface you will write code that can handle any kind of Vehicle. So in the future your code, without modification, should work with Trains and Planes.

If you ignore the interface then you are stuck with CArs and Bikes, and any new Vehicles will require additional code modifications.

The principle behind this is:

Open to Extension, Closed to Modification.


Because you don't really care what the implementation is... only what it's behavior is.

Say you have an animal

interface Animal {
    String speak();
}

class Cat implements Animal {

    void claw(Furniture f) { /* code here */ }

    public String speak() { return "Meow!" }
}

class Dog implements Animal {

    void water(FireHydrant fh) { /* code here */ }

    public String speak() { return "Woof!"; }
}

Now you want to give your kid a pet.

Animal pet = new ...?
kid.give(pet);

And you get it back later

Animal pet = kid.getAnimal();

You wouldn't want to go

pet.claw(favorateChair);

Because you don't know if the kid had a dog or not. And you don't care. You only know that --Animals-- are allowed to speak. You know nothing about their interactions with furniture or fire hydrants. You know animals are for speaking. And it makes your daughter giggle (or not!)

kid.react(pet.speak());

With this, when you make a goldfish, the kid's reaction is pretty lame (turns out goldfishes don't speak!) But when you put in a bear, the reaction is pretty scary!

And you couldn't do this if you said

Cat cat = new Cat();

because you're limiting yourself to the abilities of a Cat.


Honestly your argument is rather moot. What's happening here is an implicit conversion to an IVehicle. You and your friend seem to be arguing about whether it's better to do it immediately (as per the first code listing), or later on (when you call the method, as per the second code listing). Either way, it's going to be implicitly converted to an IVehicle, so the real question is -- do you need to deal with a Car, or just a Vehicle? If all you need is an IVehicle, the first way is perfectly fine (and preferable if at a later point you want to transparently swap out a car for a bike). If you need to treat it like a car at other points in your code, then just leave it as a car.


Declaring interfaces and instantiating them with objects allows for a powerful concept called polymorphism.

List<IVehicle> list = new ArrayList<IVehicle>();
list.add(new Car());
list.add(new Bike());

for (int i = 0; i < list.size(); ++i)
    list.get(i).doSomeVehicleAction();   // declared in IVehicle and implemented differently in Car and Bike

To explicitly answer the question: You would use an interface declaration (even when you know the concrete type) so that you can pass multiple types (that implement the same interface) to a method or collection; then the behavior common to each implementing type can be invoked no matter what the actual type is.


well interfaces are behaviors and classes are their implementation so there will be several occasions later when you will program where you will only know the behaviors(interface). and to make use of it you will implement them to get benefit out of it. it is basically used to hiding implementation details from user by only telling them the behavior(interface).


Your intuition is correct; the type of a variable should be as specific as possible.

This is unlike method return types and parameter types; there API designers want to be a little abstract so the API can be more flexible.

Variables are not part of APIs. They are implementation details. Abstraction usually doesn't apply.


Even in 2022, it's confusing to understand the true purpose of an interface even to a trained eye who didn't start his/her career in java.

After reading a lot of answers in various online posts, I think that an interface is just a way to not care about the implementation details of a certain activity which is being passed down to a common goal (a certain method). To make it easy, a method doesn't really care how you implement your operations but only cares about what you pass down to it.

The OP is correct in a way to ask why we couldn't just reference to the type of the concrete class than to use an interface. But, we cannot think or understand the use case of an interface in a isolated pov. Most explanation won't justify it's use unless you look at how classes like ArrayList and LinkedList are derived. Here is my simple explanation.

Class CustomerDelivery {

line 2 -> public void deliverMeMyIphone( DeliveryRoutes x //I don't care how you deliver it){
         //Just deliver to my home address.
    }
line 3 -> DeliveryRoutes a = new AmazonDelivery();
               DeliveryRoutes b = new EbayDelivery();
 
//sending IPhone using Amazon Delivery. Final act.
deliverMeMyIphone(a.route());
 
//sending IPhone using eBay Delivery. Final act
deliverMeMyIphone(b.route());
}

Interface DeliveryRoutes {

    void route(); // I dont care what route you take, and also the method which takes me as an argument won't care and that's the contract.

}
Class AmazonDelivery implements DeliveryRoutes {
   @overide route() {
           // Amazon guy takes a different route
     }
}

Class EbayDelivery implements DeliveryRoutes {
   @overide route() {
           // ebay guy takes a different route
     }
}

From the above example In line 2, just imagine to yourself what would happen if you cast the type of value x to a concrete class like AmazonDelivery and not the interface DeliveryRoutes type? or what would happen in line 3 if you change the type from the interface to AmazonDelivery type? It would be a mess. Why? because the method deliverMeMyIphone will be forced to work with only one type of delivery i.e AmazonDelivery and won't accept anything else.

Most answers confuse us with by saying Interfaces helps in multiple inheritance which is true, don't get me wrong, but it's not the only story.


With "IVehicle modeOfTransport1 = new Car();", methods owned only by Car are not accessible to modeOfTransport1. I don't know the reason anyway.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜