In my quest to correctly grasp Interface best practices, I have noticed declarations such as:
List<String> myList = new ArrayList<String>();
instead of
ArrayList<String> myList = new ArrayList<String>();
-To my understanding the reason is because it allows flexibility in case one day you do not want to implement an ArrayList but maybe another type of list.
With this logic, I set up an example:
public class InterfaceTest {
public static void main(String[] args) {
PetInterface p = new Cat();
p.talk();
}
}
interface PetInterface {
public void talk();
}
class Dog implements PetInterface {
@Override
public void talk() {
System.out.println("Bark!");
}
}
class Cat implements PetInterface {
@Override
public void talk() {
System.out.println("Meow!");
}
public void batheSelf() {
System.out.println("Cat bathing");
}
}
My question is, I cannot access the batheSelf() method because it only exists for Cat. That leads me to believe that I should only declare from an Interface if I am only going to use methods declared in the Interface (and not extra methods from the subclass), otherwise I should declare from the Class directly (in this case Cat). Am I correct in this assumption?
When there is a choice between referring to an object by their
interfaceor aclass, the former should be preferred, but only if an appropriate type exists.Consider
StringimplementsCharSequenceas an example. You should not just blindly useCharSequencein preferrence toStringfor all cases, because that would deny you simple operations liketrim(),toUpperCase(), etc.However, a method that takes a
Stringonly to care about its sequence ofcharvalues should useCharSequenceinstead, because that is the appropriate type in this case. This is in fact the case withreplace(CharSequence target, CharSequence replacement)in theStringclass.Another example is
java.util.regex.Patternand itsMatcher matcher(CharSequence)method. This lets aMatcherbe created fromPatternfor not justString, but also for all otherCharSequencethere are out there.A great example in the library of where an
interfaceshould’ve been used, but unfortunately wasn’t, can also be found inMatcher: itsappendReplacementandappendTailmethods accept onlyStringBuffer. This class has largely been replaced by its faster cousinStringBuildersince 1.5.A
StringBuilderis not aStringBuffer, so we can not use the former with theappend…methods inMatcher. However, both of themimplementsAppendable(also introduced in 1.5). IdeallyMatcher‘sappend…method should accept anyAppendable, and we would then be able to useStringBuilder, as well as all otherAppendableavailable!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
Catexample, you may defineinterface SelfBathable, for example. Then instead of referring to aCat, you can accept anySelfBathableobject (e.g. aParakeet)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
Related links
java.util.regex.Matchershould make more use ofAppendable