So much for a type safety with generics
There is something strange I can't wrap my head about. Generics is a good thing in general. It gives a type-safety at compile time. For example:
class A{}
class B{}
static void someMethod() {
List<B> listB = new ArrayList<B>();
listB.add(B);
listB.add(A); // here goes a compiler error
// but this will compile fine
List<Object> objList = new ArrayList<Object>();
objList.add("String");
objList.add(new Integer(9));
objList.add(B);
}
Well, is it something Java developers misse开发者_JAVA技巧d?
Object is the base class of everything and you are adding objects of type Object. Therefore type safe (as all inserted objects fulfill the requirement of being of type object).
If you create a list of Objects
, you're allowed to add Objects. That's why you can add instances of String
, Integer
and B
... they are all objects!
Perhaps you wonder why one is not only allowed to add Objects
. Well, it can be quite handy to allow for subtypes to be added. Consider for instance that you want to store lots of numbers in a list. You don't know if they are floating point numbers or integers etc. Then you could do
List<Number> numList = new ArrayList<Number>();
numList.add(new Double(5.5));
numList.add(new Integer(5));
numList.add(new Long(10L));
In simple words, "String"
new Integer(9)
and B
are Object
, whereas B
is not A
.
In inheritance, if
A
extends B
, then A is said to be of type
Aand
Bas well;
Objectis the
super` class of all the class.
Essentially:
List l = new ArrayList();
is interpreted as
List<Object> l = new ArrayList<Object>();
so, in fact, adding arbitrary Objects to this list is fine. This should emit a warning when it is compiled because you are using a parameterised type with no parameters.
It was implemented this way to preserve backward compatibility. If all non-parameterised code that used new ArrayList()
(or any other collections framework class) suddenly didn't compile then that would cost an enormous amount of time and money to fix. Yes, it means you can write unsafe code, but you get warned about it and at least your existing 10-million-line application still works.
Java generics do provide proper type safety, but you are not specifying the boundaries appropriately. Here is an example what generics actually provide you with:
List<Number> list1 = new ArrayList<Number>();
list1.add(new Integer(1)); // can add anything that is a Number
list1.add(new Double(1.0));
Number n = list1.get(0); // and retrieve Numbers
List<? super Number> list2 = new ArrayList<Number>(); // can be a list of number
list2 = new ArrayList<Object>(); // or a list of objects
list2.add(new Integer(1)); // but can only add numbers
list2.add(new Double(1.0));
Object o1 = list2.get(0); // and only retrieve Objects
List<? extends Number> list3 = new ArrayList<Number>(); // can be a list of numbers
list3 = new ArrayList<Integer>(); // or any subtype of Number
list3 = new ArrayList<Double>();
list3.add(null); // but can add any thing to it
Number n2 = list3.get(0); // and only retrieve Numbers
the reason your first example fails is because A is not a
B`.
the reason the second example works is because String
, new Integer(9)
, and B
are all Object
s.
This makes good logical sense, and practical for the reasons other posters said.
精彩评论