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.