Java Interface vs Concrete Class for Parameterized Type

Overview

For parameterized type, one has to stick strictly to the same type declaration when creating the object. Do not replace an interface with a concrete class.

Java Interface vs Concrete Class for Parameterized Type

This post comes from an interesting Java code statement like the following:

// (1) error:
//     cannot instantiate the type List< List<Integer> >
List< List<Integer> > t = new List< List<Integer> >();

// (2) error: type mismatch,
//     cannot convert from ArrayList< ArrayList<Integer> > to List< List<Integer> >
List< List<Integer> > t = new ArrayList< ArrayList<Integer> >();

// (3) correct
List< List<Integer> > t = new ArrayList< List<Integer> >();

Here are some important lessons we can learn from the above error:

  1. List is a Java interface, ArrayList is a concrete class which implements List, we cannot create instance of List but we can create instance of ArrayList, this is why we got the error in (1)
  2. Do not steal the concept in (1) and apply it in (2) for the parameterized type, for the paremeterized type inside the angle brackets for generics, it is purely a type declaration, there is no concept of instantiating at this place, and the compiler just check whether the type declaration is consistent of not, so the type inside the angle brackets has to be the same of List
  3. When we add new instances, we still need to use the concrete class ArrayList as t.add(new ArrayList()); This is about instantiating again, rather than type checking.
  4. So do not confuse the Java interface and concrete class in different cases of instantiating and parameterized type checking.

Since we now bring up both the Java interface and generics concepts, I would like to do a bit recap and highlight some important facts about them.

Basic Knowledge about Java Interface Recap

1)  Java interface could be treated as “pure” abstract classes, and it cannot have any instances.

2) If Class A implements Interface B, then A has to implement all the methods declared in B to be a normal class. Otherwise, it would be compile error to require you to implement all the abstract methods in B. An incorrect thought would be that, it is legal and Java would treat A as interface too.  Actually the keyword implements already forces the behavior of A to implement all the methods.

3) Compared with abstract class (quite similar to the ‘virtual’ keyword in C++):

  • If B is an abstract class, and A extends B, if A is a normal class, A is forced to implement all the abstract methods, otherwise, A has to be declared as abstract class too explicitly, do not think Java would automatically treat A as abstract class, if you do not use abstract to declare it, you got compile error.
  • A class containing abstract methods is called and has to be declared explicitly as an abstract class.
  • It is possible to make a class abstract without including any abstract methods (to prevent any instances of this class).
  • Abstract class can contain concrete methods with body of definition but in Java interface, such concrete methods are not allowed, that is, Java interface only contain abstract methods without definition.

4) By default, the methods in an interface is public, and it is illegal to put the private modifier before the method in Java interface.

5) An interface can also contain fields, but these are implicitly static and final. As a result, such fields have to be initialized at the same time as its declaration. And again, we cannot put private modifier before these fields in Java interface.

Basic Knowledge about Java Generics (Parameterized Type) Recap

1) Java containers could contain different objects, and you can put a Dog into a collection of Cats

2) Until JDK 6.0, one still can use Java Containers as raw type and add even different class objects into the same container say into a Java ArrayList.

3) By using Generics, you make a type-safe container and Java will do type checking at compile time. The difference between raw type and the parameterized container is that, by using generics and specify the type parameter inside the angle brackets:

  • The compiler will check the type for the objects you try to put into the container, and it is a different one, it will report compile error, but normal upcasting still works, say you declare ArrayList, you can definitely put say JapaneseApple which is a subclass of Apple into that container
  • You do not have to do the cast explicitly as in raw type cases. That is, if you use raw type say List l = new ArrayList(), and then put and Apple object into l, when you need to access the object, you have to cast it since l only knows it contains Objects, that is do this: (Apple)l.get(0).

4) new ArrayList() vs new ArrayList() and which one is a good practice? consider the following code

List<String> t = new ArrayList<String>();

And note the following:

  • Generic List<String> and ArrayList<String> types are used instead of raw ArrayList type.
  • list is declared as List<String>, i.e. the interface type instead of implementation type ArrayList<String>.

5) Don’t use raw types

  • JLS 4.8 Raw Types
    > The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

  • Effective Java 2nd Edition: Item 23: Don’t use raw types in new code
    > If you use raw types, you lose all the safety and expressiveness benefits of generics.

6) Prefer interfaces to implementation classes in type declarations

  • Effective Java 2nd Edition: Item 52: Refer to objects by their interfaces
    > […] you should favor the use of interfaces rather than classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.

Summary

For parameterized type, one has to stick strictly to the same type declaration when creating the object. Do not replace an interface with a concrete class.

Written on December 7, 2014