Advanced Java 5 Generics
Let’s take one of the standard examples of an inheritance
tree: we have a superclass Animal, from which we derive subclasses Mammal and
Reptile, which are further divided into concrete subclasses such as Cat, Dog
and Snake. Now, suppose you want to create a variable pets, containing a
collection of Cat, Dog and Snake objects. If you have seen anything about Java
generics at all, you will probably guess that the code looks somewhat like
this:
Collection<Animal>
myPets = new ArrayList<Animal>();
myPets.add(new
Dog("Roger"));
myPets.add(new
Cat("Miss Fluffy"));
myPets.add(new
Snake("Hissy"));
After all, dogs, cats and snakes are all animals, so any
collection of animals should be able to accept objects of type Dog, right?
Right! There’s nothing wrong with this at all. It’s just
basic object orientation: every Dog object is by definition also an Animal
object, and can be used in any situation where an Animal is expected. That
includes putting it into a Collection of Animals. C++ programmers may start to
protest at this point, but that just demonstrates that C++ is not a very good
example of an OO language.
So, if it’s really that simple, why am I making such a big
deal out of it? Because once you start working with generics, you sooner or
later run into the “? extends” and the slightly less common “? super” syntax,
as in this example from the Java 5 run-time library:
public static
<T>
int
binarySearch(List<? extends Comparable<? super T>> list, T key) {
...
}
And once you’ve seen such an example, uncertainty sets in:
“since I want my collection to contain not just Animal objects, but Dog objects
and Cat objects as well, shouldn’t I declare my collection as Collection<?
extends Animal> just to be sure?” But as mentioned above, this is not
needed. An ordinary Collection<Animal> will do the job just fine. In
fact, you couldn’t create a Collection of Animals which does not also accept
Dog objects, even if you wanted to!
So, what is the point of the “? extends” syntax? For that,
let’s step back a bit and recall a little bit of trivia about how Java arrays
work: the fact that it is possible to trick the compiler into accepting an
invalid assignment into an array:
Animal[] dogs = new
Dog[10]; //
An array of dogs is also an array of animals..
dogs[0] = new
Cat("Miss
Fluffy"); //
..but this will break at run-time!
This will throw an exception at run-time, of course, but the
compiler won’t protest. After all, as far as the compiler knows, dogs is an
array of Animals, and a cat is an animal, so what’s the problem? The only way
to solve this would have been for the Java designers to disallow even the most
innocent-seeming conversions between arrays. Apparently, they decided that
price was too high. But now with generics, they had the opportunity to make the
whole thing excruciatingly correct for the Collections framework, and so they
did.
If you try the following:
Collection<Animal>
dogs = new ArrayList<Dog>();
you will get a compile-time error, and the array example
above explains why that is exactly as it should be. A list of dogs is not a
list of animals, because it does not support everything you should be able to
do with a list of animals. Specifically, a list of dogs does not support having
cats inserted into it. As far as the Java compiler is concerned, there simply
isn’t any kind of inheritance relation between the two collection types at all.
And this is where the “? extends” syntax comes in:
Collection<?
extends Animal> dogs = new ArrayList<Dog>();
This works, but it’s important to realize exactly what it
says. Collection<? extends Animal> does not mean “a collection of Animal
and Animal-derived objects”! Rather, it means “a collection of objects of some
unknown class and the derivatives of that class; we don’t know which class but
we know that it must be Animal or a subclass of Animal”. The important thing to
realize is that the “? extends” syntax modifies the type of the collection
object, not the type of its contents.
The even more important thing to realize is that
ArrayList<Animal> is a concrete class, whereas ArrayList<? extends
Animal> is abstract. You cannot do new ArrayList<? extends Animal>()
any more than you can instantiate an abstract class or an interface. The
variable dogs, above, may be declared as a collection of “? extends Animal”,
but at any moment in time, the object it references will always be a collection
of some concrete type — an ArrayList<Dog>, in this case. Just think of
any “? extends” or “? super” construct as a special kind of interface or
abstract class, and you’re 99% there.
So, what happens when we try to use this syntax to repeat
the bit of array trickery we did above?
Collection<?
extends Animal> dogs = new ArrayList<Dog>();
dogs.add(new
Snake("Hissy")); // This won't
compile..
dogs.add(new
Dog("Roger")); // ..and
neither will this!
dogs.add(new
Animal()); // This won't compile either.
dogs.add(null); //
This will work, though.
Because dogs is defined as Collection<? extends
Animal>, the compiler can not make any assumptions about what kind of
collection object it will actually point to at run-time. It could be an ArrayList<Goldfish>,
for all the compiler knows at this point! So, adding a Dog or Cat or Snake to
the collection would not be safe, and the compiler won’t allow it. The only
value that can’t possibly violate the collection’s contract is null, so that’s
the only value that is allowed. Try as you might, you will not be able to sneak
an object of the wrong type into a collection, without using untyped
collections or explicit casts.[Source]-https://blogs.infosupport.com/generics/
We provide the best Advanced Java training, navi mumbai. We have industry experienced
trainers and provide hands on practice. Basic to advanced modules are covered
in training sessions.
Comments
Post a Comment