Difference between List, List<?>, List<T>, List<E>, and List<Object>
What are the differences between List
, List<?>
, List<T>
, List<E>
, and List<Object>
?
1. List
List
: is a raw type, therefore not typesafe
. It will only generate a runtime error when the casting is bad. We want a compile time error when the cast is bad. Not recommended to use.
2. List<?>
List<?>
is an unbounded wildcard. But I'm not sure what it's for? I can print a List<?>
without issue:
public static void test(List<?> list){
System.out.println(list); // Works
}
W开发者_运维问答hy can't I add items to a List<?>
?
public static void test(List<?> list){
list.add(new Long(2)); // Error
list.add("2"); // Error
System.out.println(list);
}
3. List<T>
public static void test(List<T> list){ // T cannot be resolved
System.out.println(list);
}
I don't understand this syntax. I saw something like this, and it works:
public <T> T[] toArray(T[] a){
return a;
}
Sometimes, I see <T>
, or <E>
, or <U>
, <T,E>
. Are they all the same or do they represent something different?
4. List<Object>
This gives the error "The method test(List<Object>)
is not applicable for the argument List<String>
":
public static void test(List<Object> list){
System.out.println(list);
}
If I try this then I got "Cannot cast from List<String>
to List<Object>
":
test((List<Object>) names);
I am confused. String
is a subclass of Object
, so why isn't List<String>
a subclass of List<Object>
?
1) Correct
2) You can think of that one as "read only" list, where you don't care about the type of the items.Could e.g. be used by a method that is returning the length of the list.
3) T, E and U are the same, but people tend to use e.g. T for type, E for Element, V for value and K for key. The method that compiles says that it took an array of a certain type, and returns an array of the same type.
4) You can't mix oranges and apples. You would be able to add an Object to your String list if you could pass a string list to a method that expects object lists. (And not all objects are strings)
For the last part: Although String is a subset of Object, but List<String> is not inherited from List<Object>.
The notation List<?>
means "a list of something (but I'm not saying what)". Since the code in test
works for any kind of object in the list, this works as a formal method parameter.
Using a type parameter (like in your point 3), requires that the type parameter be declared. The Java syntax for that is to put <T>
in front of the function. This is exactly analogous to declaring formal parameter names to a method before using the names in the method body.
Regarding List<Object>
not accepting a List<String>
, that makes sense because a String
is not Object
; it is a subclass of Object
. The fix is to declare public static void test(List<? extends Object> set) ...
. But then the extends Object
is redundant, because every class directly or indirectly extends Object
.
The reason you cannot cast List<String>
to List<Object>
is that it would allow you to violate the constraints of the List<String>
.
Think about the following scenario: If I have a List<String>
, it is supposed to only contain objects of type String
. (Which is a final
class)
If I can cast that to a List<Object>
, then that allows me to add Object
to that list, thus violating the original contract of List<String>
.
Thus, in general, if class C
inherits from class P
, you cannot say that GenericType<C>
also inherits from GenericType<P>
.
N.B. I already commented on this in a previous answer but wanted to expand on it.
I would advise reading Java puzzlers. It explains inheritance, generics, abstractions, and wildcards in declarations quite well. http://www.javapuzzlers.com/
Let us talk about them in the context of Java history ;
List
:
List means it can include any Object. List was in the release before Java 5.0; Java 5.0 introduced List, for backward compatibility.
List list=new ArrayList();
list.add(anyObject);
List<?>
:
?
means unknown Object not any Object; the wildcard ?
introduction is for solving the problem built by Generic Type; see wildcards;
but this also causes another problem:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
List< T> List< E>
Means generic Declaration at the premise of none T or E type in your project Lib.
List< Object>
means generic parameterization.
In your third point, "T" cannot be resolved because its not declared, usually when you declare a generic class you can use "T" as the name of the bound type parameter, many online examples including oracle's tutorials use "T" as the name of the type parameter, say for example, you declare a class like:
public class FooHandler<T>
{
public void operateOnFoo(T foo) { /*some foo handling code here*/}
}
you are saying that FooHandler's
operateOnFoo
method expects a variable of type "T" which is declared on the class declaration itself, with this in mind, you can later add another method like
public void operateOnFoos(List<T> foos)
in all the cases either T, E or U there all identifiers of the type parameter, you can even have more than one type parameter which uses the syntax
public class MyClass<Atype,AnotherType> {}
in your forth ponint although efectively Sting is a sub type of Object, in generics classes there is no such relation, List<String>
is not a sub type of List<Object>
they are two diferent types from the compiler point of view, this is best explained in this blog entry
Theory
String[]
can be cast to Object[]
but
List<String>
cannot be cast to List<Object>
.
Practice
For lists it is more subtle than that, because at compile time the type of a List parameter passed to a method is not checked. The method definition might as well say List<?>
- from the compiler's point of view it is equivalent. This is why the OP's example #2 gives runtime errors not compile errors.
If you handle a List<Object>
parameter passed to a method carefully so you don't force a type check on any element of the list, then you can have your method defined using List<Object>
but in fact accept a List<String>
parameter from the calling code.
A. So this code will not give compile or runtime errors and will actually (and maybe surprisingly?) work:
public static void main(String[] args) {
List argsList = new ArrayList<String>();
argsList.addAll(Arrays.asList(args));
test(argsList); // The object passed here is a List<String>
}
public static void test(List<Object> set) {
List<Object> params = new ArrayList<>(); // This is a List<Object>
params.addAll(set); // Each String in set can be added to List<Object>
params.add(new Long(2)); // A Long can be added to List<Object>
System.out.println(params);
}
B. This code will give a runtime error:
public static void main(String[] args) {
List argsList = new ArrayList<String>();
argsList.addAll(Arrays.asList(args));
test1(argsList);
test2(argsList);
}
public static void test1(List<Object> set) {
List<Object> params = set; // Surprise! Runtime error
}
public static void test2(List<Object> set) {
set.add(new Long(2)); // Also a runtime error
}
C. This code will give a runtime error (java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
):
public static void main(String[] args) {
test(args);
}
public static void test(Object[] set) {
Object[] params = set; // This is OK even at runtime
params[0] = new Long(2); // Surprise! Runtime error
}
In B, the parameter set
is not a typed List
at compile time: the compiler sees it as List<?>
. There is a runtime error because at runtime, set
becomes the actual object passed from main()
, and that is a List<String>
. A List<String>
cannot be cast to List<Object>
.
In C, the parameter set
requires an Object[]
. There is no compile error and no runtime error when it is called with a String[]
object as the parameter. That's because String[]
casts to Object[]
. But the actual object received by test()
remains a String[]
, it didn't change. So the params
object also becomes a String[]
. And element 0 of a String[]
cannot be assigned to a Long
!
(Hopefully I have everything right here, if my reasoning is wrong I'm sure the community will tell me. UPDATED: I have updated the code in example A so that it actually compiles, while still showing the point made.)
Problem 2 is OK, because " System.out.println(set);" means "System.out.println(set.toString());" set is an instance of List, so complier will call List.toString();
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
Element ? will not promise Long and String, so complier will not accept Long and String Object
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object
Problem 3: these symbols are same, but you can give them differet specification. For example:
public <T extends Integer,E extends String> void p(T t, E e) {}
Problem 4: Collection does not allow type parameter covariance. But array does allow covariance.
You're right: String is a subset of Object. Since String is more "precise" than Object, you should cast it to use it as an argument for System.out.println().
精彩评论