Java interfaces and types - java

Let's say you have some Java code as follows:
public class Base{
public void m(int x){
// code
}
}
and then a subclass Derived, which extends Base as follows:
public class Derived extends Base{
public void m(int x){ //this is overriding
// code
}
public void m(double x){ //this is overloading
// code
}
}
and then you have some declarations as follows:
Base b = new Base();
Base d = new Derived();
Derived e = new Derived();
b.m(5); //works
d.m(6); //works
d.m(7.0); //does not compile
e.m(8.0); //works
For the one that does not compile, I understand that you are passing in a double into Base's version of the m method, but what I do not understand is... what is the point of ever having a declaration like "Base b = new Derived();" ?
It seems like a good way to run into all kinds of casting problems, and if you want to use a Derived object, why not just go for a declaration like for "e"?
Also, I'm a bit confused as to the meaning of the word "type" as it is used in Java. The way I learned it earlier this summer was, every object has one class, which corresponds to the name of the class following "new" when you instantiate an object, but an object can have as many types as it wants. For example, "e" has type Base, Derived, (and Object ;) ) but its class is Derived. Is this correct?
Also, if Derived implemented an interface called CanDoMath (while still extending Base), is it correct to say that it has type "CanDoMath" as well as Base, Derived, and Object?

I often write functions in the following form:
public Collection<MyObject> foo() {}
public void bar(Collection<MyObject> stuff){}
I could just as easily have made it ArrayList in both instances, however what happens if I later decide to make the representation a Set? The answer is I have a lot of refactoring to do since I changed my method contract. However, if I leave it as Collection I can seamlessly change from ArrayList to HashSet at will. Using the example of ArrayList it has the following types:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess

There are a number of cases where confining yourself to a particular (sub)class is not desired, such as the case you have where e.m(8.0);. Suppose, for example, you have a method called move that moves an object in the coordinate graph of a program. However, at the time you write the method you may have both cartesian and radial graphs, handled by different classes.
If you rely on knowing what the sub-class is, you force yourself into a position wherein higher levels of code must know about lower levels of code, when really they just want to rely on the fact that a particular method with a particular signature exists. There are lots of good examples:
Wanting to apply a query to a database while being agnostic to how the connection is made.
Wanting to authenticate a user, without having to know ahead of time the strategy being used.
Wanting to encrypt information, without needing to rip out a bunch of code when a better encryption technique comes along.
In these situations, you simply want to ensure the object has a particular type, which guarantees that particular method signatures are available. In this way your example is contrived; you're asking why not just use a class that has a method wherein a double is the signature's parameter, instead of a class where that isn't available. (Simply put; you can't use a class that doesn't have the available method.)
There is another reason as well. Consider:
class Base {
public void Blah() {
//code
}
}
class Extended extends Base {
private int SuperSensitiveVariable;
public setSuperSensistiveVariable(int value) {
this.SuperSensistiveVariable = value;
}
public void Blah() {
//code
}
}
//elsewhere
Base b = new Extended();
Extended e = new Extended();
Note that in the b case, I do not have access to the method set() and thus can't muck up the super sensitive variable accidentally. I can only do that in the e case. This helps make sure those things are only done in the right place.
Your definition of type is good, as is your understanding of what types a particular object would have.

What is the point of having Base b = new Derived();?
The point of this is using polymorphism to change your implementation. For example, someone might do:
List<String> strings = new LinkedList<String>();
If they do some profiling and find that the most common operation on this list is inefficient for the type of list, they can swap it out for an ArrayList. In this way you get flexibility.
if you want to use a Derived object
If you need the methods on the derived object, then you would use the derived object. Have a look at the BufferedInputStream class - you use this not because of its internal implementation but because it wraps an InputStream and provides convenience methods.
Also, I'm a bit confused as to the meaning of the word "type" as it is used in Java.
It sounds like your teacher is referring to Interfaces and Classes as "types". This is a reasonable abstraction, as a class that implement an interface and extends a class can be referred to in 3 ways, i.e.
public class Foo extends AbstractFoo implements Comparable<Foo>
// Usage
Comparable<Foo> comparable = new Foo();
AbstractFoo abstractFoo = new Foo();
Foo foo = new Foo();
An example of the types being used in different contexts:
new ArrayList<Comparable>().Add(new Foo()); // Foo can be in a collection of Comparable
new ArrayList<AbstractFoo>().Add(new Foo()); // Also in an AbstractFoo collection

This is one of the classic problems on object oriented designs. When something like this happens, it usually means the design can be improved; there is almost always a somewhat elegant solution to these problems....
For example, why dont you pull the m that takes a double up into the base class?
With respect to your second question, an object can have more than one type, because Interfaces are also types, and classes can implement more than one interface.

Related

Change flow of static call (Java)

I have many instances of
Foo.a()
but now I want to split up calls to a() based on certain criteria. If possible I would like to keep the Foo.a() calls unchanged. Instead, perhaps Foo could become a factory that manages the flow and FooA and FooB could extend Foo. For example, in Foo:
private static Class<?> foo;
static {
if (certain_criteria) {
foo = SomeUtil.getClass("FooA");
} else {
foo = FooB.class;
}
Object obj = foo.newInstance();
o = (Foo) obj;
}
...
public static void a() {
o.a(); //And this should call either FooA.a() or FooB.a()
//But a() should be accessed in a static way
}
I can't make a() in Foo non-static because then I'll have to change the 100+ calls throughout the project to Foo.a(). Is there a way around this? Or a better way to handle the flow?
I also tried to use foo to call a(), but that gives a compiler error because it is of type Class?>. If I change it to
Class<Foo>
then I get
Type mismatch: cannot convert from Class<FooB> to Class<Foo>
You propose using static method Foo.a() as a facade over selecting and invoking an appropriate implementation, in a configurable manner chosen by class Foo. Your specific idea seems to rely on subclasses of Foo to implement the Strategy pattern for supporting Foo.a().
You are conflating at least two separable pieces to this:
the strategy for implementing Foo.a(), and
the mechanism by which a specific strategy is chosen and instantiated.
In particular, although you may have reason to want to use subclasses of Foo to represent your strategies in the real code, no such reason is apparent in your example code. Schematically, then, you seem to want something like this:
public class Foo {
private static FooStrategy strategy = FooStrategyFactory.createStrategy();
public static void a() {
strategy.doA();
}
}
interface FooStrategy {
void doA();
}
You don't need to go all the way there, of course. Your original idea was basically to let Foo itself serve in the place of FooStrategy, and to let a static initializer serve instead of a separate FooStrategyFactory. There's nothing inherently wrong with that; I just pull it apart to more clearly show what role each bit serves.
You also expressed some specific implementation issues:
If I change it to Class<Foo> then I get
Type mismatch: cannot convert from Class to Class
The equivalent in my scheme above would be declaring a variable of type Class<FooStrategy>, and attempting to assign to it a Class<FooStrategyA> representing a class that implements FooStrategy. The correct type for a Class object that may represent any class whose instances are assignment-compatible with type FooStrategy is Class<? extends FooStrategy>. That works whether FooStrategy itself is a class or an interface.
I can't call any classes from Foo on foo. "The method a() is undefined for the type Class"
You seem to have been saying that you could not invoke static methods of class Foo on an object of type Class<? extends Foo>. And indeed, you can't. Objects of class Class have only the methods of class Class. Although you can use them to reflectively invoke methods of the classes they represent, such methods are not accessible directly via the Class instance itself. That issue does not arise directly in the scheme I presented, but it could arise in the factory or strategy implementations.
Moreover, static methods are not virtual. They are bound at compile time, based on the formal type of the reference expressions on which they are invoked. In order to apply the strategy pattern correctly, the needed strategy implementation methods need to be virtual: non-private and non-static.

Java inheritance vs initialization

I'm reading J. Bloch's Effective Java and now I'm at inheritance vs composition section. As far as I understood he said that inheritance is not always good.
A related cause of fragility in subclasses is that their superclass
can acquire new methods in subsequent releases. Suppose a program
depends for its security on the fact that all elements inserted into
some collection satisfy some predicate. This can be guaranteed by
subclassing the collection and overriding each method capable of
adding an element to ensure that the predicate is satisfied before
adding the element. This works fine until a new method capable of
inserting an element is added to the superclass in a subsequent
release.
But why doesn't it work? The superclass is just the Collection interface and if we add a new method we just a compile-time error. That's not harmful ever...
Suppose you have a Collection superclass in some library v1.0:
public class MyCollection {
public void add(String s) {
// add to inner array
}
}
You subclass it in order to only accept Strings that have length 5:
public class LimitedLengthCollection extends MyCollection {
#Override
public void add(String s) {
if (s.length() == 5) {
super.add(s);
}
}
}
The contract, the invariant of this class is that it will never contain a String that doesn't have length 5.
Now version 2.0 of the library is released, and you start using it. The base class is modified to:
public class MyCollection {
public void add(String s) {
// add to inner array
}
public void addMany(String[] s) {
// iterate on each element and add it to inner array
}
}
and your subclass is left unmodified. Now users of your subclass can do
LimitedLengthCollection c = new LimitedLengthCollection();
c.addMany(new String[] {"a", "b", "c"});
and the contract of your subclass is thus broken. It was supposed to only accept Strings of length 5, and it doesn't anymore, because an additional method has been added in the superclass.
The problem is not that inheritance could not work.
The problem is that with inheritance the developer can not enforce some behaviour (like the example of the collection that satisfy some predicate) .
When we create a new class rarely it really is a specialized type of another. More often it is something new that use other classes.
So rarely we need inheritance and more often we need to create a class that use other classes to so something.
The IS A vs HAS A
You have to ask yourself:
Class B IS A new sub type of Class A that do the same things of A in different ways ?
or
Class B HAS A class inside to do something different from
what A is intented to do ?
And know that more often the right answer the latter.
if we add a new mehtod we just a compile-time error
That is true only when an abstract method is added to the superclass/interface. If a non-abstract method is added, it is perfectly valid not to override that new method.
Because it (in general) will break the client code that has implemented the Collection class.
In this particular example the security will be broken because malicious users would be able to insert items by using the non yet overridden method that was added after you have shipped your code.
Basing your code on inheriting classes you do not control may bite you in the future.

Why is `Class` class final?

Answering a question here at SO, I came up to a solution which would be nice if it would be possible extend the Class class:
This solution consisted on trying to decorate the Class class in order to allow only certain values to be contained, in this case, classes extending a concrete class C.
public class CextenderClass extends Class
{
public CextenderClass (Class c) throws Exception
{
if(!C.class.isAssignableFrom(c)) //Check whether is `C` sub-class
throw new Exception("The given class is not extending C");
value = c;
}
private Class value;
... Here, methods delegation ...
}
I know this code does not work as Class is final and I wonder why Class is final. I understand it must have to do with security but I can't imagine a situation where extending Class is dangerous. Can you give some examples?
By the way, the closer solution for the desired behavior I can reach is:
public class CextenderClass
{
public CextenderClass(Class c) throws Exception
{
if(!C.class.isAssignableFrom(c)) //Check whether is `C` sub-class
throw new Exception("The given class is not extending C");
value = c;
}
public Class getValue() {
return value;
}
private Class value;
}
But it lacks the beauty of transparent decoration.
According to comments in the Class class Only the Java Virtual Machine creates Class
objects..
If you were allowed to extend classes, then a problem would arise, should you be allowed to create custom Class objects?. If yes, then it would break the above rule that only the JVM should create Class Objects. So, you are not given that flexibility.
PS : getClass() just returns the already created class object. It doesn't return a new class object.
That's a very good question but is only an instance of a more general problem and although #TheLostMind 4 years ago has just responded (considered an implementation point of view) in terms of JVM rules/restrictions the problem still remain: why JVM pose that rules? There have to be a plausible reason. We have to examine it from a more abstract level(point of view)
Short answer:
Plausibly all that has to do with type safety and as all we know Java is a strongly typed language and permits no one to change that fact.
Elaborate Answer(with some context so to be understandable for everyone):
All the story start from static and dynamic binding.
The flexibility given by subtype polymorphism makes the declared (static) type of an object in general different from its run-time (dynamic) type.
The run-time type is in general a subtype of the static type.
e,g.
AClass a = new AClass();
AsubClass b = new AsubClass(); // AsubClass is derived from AClass
a=b;
The static type of a is AClass and after the assignment a=b; its runtime type is AsubClass. This has implications on selection of the most appropriate method when executing a message.
Now consider the class Vehicle given
below.
public class Vehicle {
private int VIN; // Vehicle Identification Number
private String make;
public boolean equals(Object x) {
return (VIN == (Vehicle)x.VIN);
}
// other methods
}
The method equals in the root class java.lang.Object is defined as the test on object identity.
This is the only meaningful way of defining the equality of objects in general.
That is, two objects are equal if they have the same identity.
In a specific class a more suitable meaning of equality may be more appropriate. In the above class two vehicles are considered equal if their VINs (vehicle identification numbers) are equal.
So the method equals is redefined accordingly in the
class Vehicle. This redefinition of an inherited
method is called overriding.
Note that the signatures of the inherited method arguments are required to remain the same in the subclass according to the function subtyping rule.
This creates an awkward situation because in the class Vehicle we would like to refer to the VIN field of the argument, and Object does not have such a field. This is why the type cast (Vehicle)x specifies that the intent is to view x as a Vehicle. There is no way to
verify this cast statically, hence a dynamic check is generated by the compiler. This is an instance of dynamic type checking.
In order for overriding to work correctly the method to be invoked is determined by the dynamic type of the receiver object (also known as dynamic dispatch (selection) of methods and is the most important case of dynamic binding in OO languages.)
e.g.
Object a = new Object();
Object b = new Object();
Vehicle aV = new Vehicle();
Vehicle bV = new Vehicle();
a=aV;
b=bV;
. . .
a.equals(b)
. . .
The method to be invoked in response to the message a.equals(b) will be the method equals overridden in the class Vehicle because the run time type of a is Vehicle.
There are situations in which overriding a method might be problematic and should not be allowed. A good example is the Java.lang.Object 's getClass() . This method has a particular implementation in the underlying virtual platform, which guarantees that invocation of this method will indeed return the class object of the receiver of the method.
Allowing overriding would have serious implications on the intended semantics of this method creating nontrivial problems in dynamic type checking. This is probably why the getClass() is declared as final.
e.g.
public class Object {
public final Class getClass();
....
}
Finally The class Class in Java is final, i.e. cannot be extended, and hence none of its methods can be overridden. Since the class Class has only introspection methods, this guarantees safety of the type system at run-time, i.e., the type information cannot be mutated at run time.
to extend the concept a bit more ...
Dynamic dispatch (selection) of methods based on the type of the receiver object is the basic technique in object-oriented languages.
It brings the type of flexibility that makes the whole object-oriented paradigm work.
Adding new types by inheritance to an already compiled and running application requires only compilation and linking of the newly introduced types without recompiling the existing application. However, this flexibility comes with some penalty in efficiency because the decision about method selection is postponed to runtime. Modern
languages have efficient techniques for dynamic dispatch of methods, but some languages like C++ and C# try to avoid the associated cost by providing a static binding (method selection) option. In C#, methods are statically bound unless they are explicitly declared as virtual.
e.g.
public class Object {
public virtual boolean equals(Object x);
// other methods
}
Overriding this method in C# will be indicated by an explicit keyword override.
e.g.
public class Vehicle {
private int VIN;
private String make;
public override boolean equals(Object x) {
return (VIN == (Vehicle)x.VIN);
}
// other methods
}
Methods whose receiver is the class object are always bound statically.
The reason is that there is only one class object for all objects of that class. Since the receiver is known at compile time, there is no need to postpone method selection to run time. These methods are thus declared as static to indicate that they belong to the class itself.
An example is the method numberOfVehicles of the class Vehicle The number of vehicles is not the property of individual vehicle objects. It is the
property of all objects of the class Vehicle, hence it belongs to the class itself.
e.g.
public class Vehicle {
// fields;
public static int numberOfVehicles();
// other methods
}
We can summarize all the above discussion as follows:
– The basic mechanism for selecting a method for executing a message (method
dispatch) in object-oriented languages is dynamic. It is based on the run-time type of the receiver object.
– The receiver of a static (i.e. class) method is the class object. Since there is only
one class object of a given type, selection of a static method is static.
– Some languages (C++ and C#) allow a choice of static versus dynamic method dispatch. Although this is done for the reasons of efficiency, it has been shown that when both dispatch mechanisms are used in a program, that may obscure the
meaning of the program.

composition-and-forwarding approach for a class with two Lists

I have read Item 16 from Effective Java and
Prefer composition over inheritance? and now try to apply it to the code written 1 year ago, when I have started getting to know Java.
I am trying to model an animal, which can have traits, i.e. Swimming, Carnivorous, etc. and get different type of food.
public class Animal {
private final List<Trait> traits = new ArrayList<Trait>();
private final List<Food> eatenFood = new ArrayList<Food>();
}
In Item 16 composition-and-forwarding reuseable approach is suggested:
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) {this.s = s;}
//implement all interface methods
public void clear() {s.clear();}
//and so on
}
public class InstrumentedSet<E> extends ForwardingSet<E> {
//counter for how many elements have been added since set was created
}
I can implement ForwardingList<E> but I am not sure on how I would apply it twice for Animal class. Now in Animal I have many methods like below for traits and also for eatenFood. This seems akward to me.
public boolean addTrait (Trait trait) {
return traits.add(trait);
}
public boolean removeTrait (Trait trait) {
return traits.remove(trait);
}
How would you redesign the Animal class?
Should I keep it as it is or try to apply ForwardingList?
There is no reason you'd want to specialize a List for this problem. You are already using Composition here, and it's pretty much what I would expect from the class.
Composition is basically creating a class which has one (or usually more) members. Forwarding is effectively having your methods simply make a call to one of the objects it holds, to handle it. This is exactly what you're already doing.
Anyhow, the methods you mention are exactly the sort of methods I would expect for a class that has-a Trait. I would expect similar addFood / removeFood sorts of methods for the food. If they're wrong, they're the exact sort of wrong that pretty much everyone does.
IIRC (my copy of Effective Java is at work): ForwardingSet's existence was simply because you cannot safely extend a class that wasn't explicitly designed to be extended. If self-usage patterns etc. aren't documented, you can't reasonably delegate calls to super methods because you don't know that addAll may or may not call add repeatedly for the default implemntation. You can, however, safely delegate calls because the object you are delegating to will never make a call the wrapper object. This absolutely doesn't apply here; you're already delegating calls to the list.

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.

Categories