Java - declaring from Interface type instead of Class
When there is a choice between referring to an object by their interface
or a class
, the former should be preferred, but only if an appropriate type exists.
Consider String
implements
CharSequence
as an example. You should not just blindly use CharSequence
in preferrence to String
for all cases, because that would deny you simple operations like trim()
, toUpperCase()
, etc.
However, a method that takes a String
only to care about its sequence of char
values should use CharSequence
instead, because that is the appropriate type in this case. This is in fact the case with replace(CharSequence target, CharSequence replacement)
in the String
class.
Another example is java.util.regex.Pattern
and its Matcher matcher(CharSequence)
method. This lets a Matcher
be created from Pattern
for not just String
, but also for all other CharSequence
there are out there.
A great example in the library of where an interface
should've been used, but unfortunately wasn't, can also be found in Matcher
: its appendReplacement
and appendTail
methods accept only StringBuffer
. This class has largely been replaced by its faster cousin StringBuilder
since 1.5.
A StringBuilder
is not a StringBuffer
, so we can not use the former with the append…
methods in Matcher
. However, both of them implements
Appendable
(also introduced in 1.5). Ideally Matcher
's append…
method should accept any Appendable
, and we would then be able to use StringBuilder
, as well as all other Appendable
available!
So we can see how when an appropriate type exists referring to objects by their interfaces can be a powerful abstraction, but only if those types exist. If the type does not exist, then you may consider defining one of your own if it makes sense. In this Cat
example, you may define interface SelfBathable
, for example. Then instead of referring to a Cat
, you can accept any SelfBathable
object (e.g. a Parakeet
)
If it does not make sense to create a new type, then by all means you can refer to it by its class
.
See also
- Effective Java 2nd Edition, Item 52: Refer to objects by their interfaces
If appropriate interface types exist, then parameters, return values, and fields should all be declared using interface types. If you get into the habit of using interface types, your program will be much more flexible. It is entirely appropriate to refer to an object by a class if no appropriate interface exists.
Related links
- Bug ID: 5066679 -
java.util.regex.Matcher
should make more use ofAppendable
Writing a method to accept interface type instead of class type
Method declaration like following should work -
public static Frame createPlot(String title, String xAxisLabel, String yAxisLabel,
List<? extends XYPlottable> points, boolean fitLine) {
Note the change in the parameter List<XYPlottable>
to List<? extends XYPlottable>
- This is called as wildcards.
Read more about generic wildcards here
How to declare a variable of type Interface and then assign to the variable an object of a Class that implements the Interface, and how to test this?
(I will use uppercase for the start of class and interface names)
Is my method valid in terms of declaring a variable of interface example and then assigning to the variable an object of exampleImpl that implements example?
Yes, but in this case it isn't needed and you shouldn't do it like this anyway. You assume that the object coming via the parameter e
will always be a ExampleImpl
instance because you have a hard coded cast of that variable. If it is not such a class you will get a ClassCastException
. In this case you can remove the
Example eInst = (ExampleImpl)e;
line and use the variable e
instead.
It must also return a variable of type example, but I do not know where newVar1 and newVar2 go when this happens since they are not defined in the interface.
You already have written the code
Example summedE = new ExampleImpl(normalisedNewVar1, normalisedNewVar2);
return summedE;
which will return an object, which implements the Example
interface. So everything is fine here. If you want to use newVar1
and newVar2
depends on your implementation and requirement for the manipulate
method.
Can I create an object within the test class to call this test on?
Yes, that's the normal way to do so. Write
Example obj = new ExampleImpl(4, 5);
in your test method to create an Example
object you can work with. You can call the manipulate()
method with a different Example
object like this:
Example obj = new ExampleImpl(4, 5);
Example obj2 = new ExampleImpl(10, 20);
Example obj3 = obj.manipulate(obj2);
I want to check it returns the right manipulation based on an object "this" and the new object "e". Is there a way to do this?
That depends on what other methods are defined on your Example
interface. You have written that it extends from a different interface, also named Example
. Base on what is defined in that interface, you might call other methods on your Example
object like this:
Assert.assertEquals(42, obj3.getDifference()); // or whatever other methods you have
Interfaces implicitly declaring public methods of Object class?
What is the purpose of this design?
Because you want to be able to call all the Object
methods ( toString
, equals
, hashCode
, and such) on every object of any type.
interface Foo {
}
Foo foo = ...;
foo.equals(otherFoo);
Doesn't matter if I actually declared the equals
method in the interface.
Interface as a type in Java?
Let's declare two interfaces and a class that implements them both:
interface I1 { }
interface I2 { }
class C implements I1, I2 { }
objects can have multiple types
In the following code, it can be seen that a C
instance has the type of C
as well as I1
and I2
:
C c = new C();
boolean isC = (c instanceof C); //true
boolean isI1 = (c instanceof I1); //true
boolean isI2 = (c instanceof I2); //true
Now let's declare a class B
which implements I1
as well:
class B implements I1 { }
if a variable is declared to be the type of an interface, its value can reference any object that is instantiated from any class that implements the interface.
If we declare a variable of type I1
, we can set it to an instance of C
, and then reassign it to an instance of B
:
I1 i1 = new C();
i1 = new B();
We can also reassign it to an instance of D
, where D
extends C
:
i1 = new D();
...
class D extends C { }
Initialize a Java Collection in Class that was declared in Interface
Java, the language, is designed with the following ideas in mind:
Fields are never really considered as part of a type's "public API". Yes, a public field can be written and can be modified from the outside, but almost no java code out there does this, and many language features simply do not support it. For example, method references exists (
someExpr::someMethodName
is legal java), but the there is no such thing as a field reference.Interfaces exist solely to convey API - to convey what a type is supposed to be able to do, it is not a thing that was intended for conveying how it is to do it. In other words, interface (hence the name) yes, implementation no.
Add these 2 concepts together and you see why what you want (put a field in an interface) is not supported. A field is not part of an interface (rule #1), therefore it must be implementation, and interfaces can not carry implementation details (rule #2), hence: You cannot put a field in an interface.
As a consequence, the java lang spec dictates that any field declaration in an interface is inherently public static final
- it's a constant. You can't make it not-public-static-final - putting private
on it as modifier is a compiler error. There is no modifier that means 'unstatic'.
You have 2 options:
You intended that field solely as implementation: You decided that all classes that implement this interface will need this field, so, might as well declare it in the interface and save the typing, right? Well, what you're doing there is grouping common aspects of the implementation together. Java supports this, but only in abstract classes. This happens all the time in java: There is
java.util.List
(an interface), andjava.util.AbstractList
(an abstract class that implements that interface, that provides a bunch of common behaviour), andjava.util.ArrayList
(the most commonly used implementation. It extends AbstractList).You actually need the 'interface' part of that field: You want to write some code, either in the interface file itself (via
default
methods), or in code that receives an object whose type is yourmachine
interface, and want to directly access the field. When in rome, act like romans. When programming java, act like other java programmers. Which means: Don't treat fields as interface - as API. If the very essence of 'a machine' (that is to say: An object that is an instance of a class, and that class implementsmachine
) is that it can provide a list of integers representing thetemp
concept, then make that part of the interface: WriteVector<Integer> getTemp;
in the interface, and let each implementing class provide an implementation. If you expect that all implementing classes likely just want:
private Vector<Integer> temp;
public Vector<Integer> getTemp() {
return temp;
}
Then you can put that in an abstract class which implements the interface, and have all your impls implement the abstract class.
If this is all flying over your head, I can make it simpler:
- All field decls in interfaces are necessarily
public static final
, whether you write it or not. You can't declare fields that aren't, the language simply won't let you. - There are good reasons for that - see the above explanation.
NB:
You appear to be using some extremely outdated (at least 25 years at this point!!) guidebook. This code wouldn't pass any review as a consequence. Vector
is obsolete and should not be used, and has been obsolete for 20 years: You want ArrayList
, presumably. Java conventions dictate that TypesGoLikeThis, methodNamesLikeThis, variableNamesAlsoLikeThis, and CONSTANTS_LIKE_THIS. Thus, it'd be public interface Machine
, not public interface machine
. The compiler lets you do whatever you want, but your code is needlessly weird and inconvenient when you integrate it with any other java code if you fail to follow the conventions.
Why would you declare an Interface and then instantiate an object with it in Java?
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.
When I create an object from a class that extends a specific interface, can I use this object in the place where I use this interface?
Yes, you can do that.
Interfaces are a way in Java to avoid multiple inheritance, i.e. declaring a class Foo
to extend a class Bar
and implement an interface Bar
means that we have a "is a" relationship between Foo
and Bar
/Baz
. So, "Foo
is a Bar
" and "Foo
is a Baz
" are true or in your case Final
is a One
. So, if a type is a subtype (or in Java implements an interface) the subtype can be used in place of the type or the interface (see the Liskov substitution principle).
When you declare a method m1(One one)
, you require the first parameter to be of type One
and as Final
is a One
this is obviously true.
Please note, that even though you pass in an object of type Final
the method only "sees" the interface part of the object without casting it.
Related Topics
How to Solve the "Double-Checked Locking Is Broken" Declaration in Java
Java Output Formatting for Strings
Using File.Listfiles with Filenameextensionfilter
Why Doesn't Java Throw an Exception When Dividing by 0.0
Calculate Elapsed Time in Java/Groovy
Custom JSON Deserialization with Jackson
Background Timer Task in Jsp/Servlet Web Application
Java.Sql.Sqlexception: No Suitable Driver Found for Jdbc:Microsoft:Sqlserver
How Can a Primitive Float Value Be -0.0? What Does That Mean
How to Use an Arraylist as a Prepared Statement Parameter
Meaning of the Import Statement in a Java File
Static VS Instance Variables: Difference
How to Cast a Double to an Int in Java by Rounding It Down