IT
What are the differences between List, List<?>, List<T>, List<E>, and List<Object>?
List
List<?>
List<T>
List<E>
List<Object>
Now I do not blindly ask this question, so please don't close this thread. Let me first introduce the base code:
private static List<String> names = new ArrayList<String>(); static { names.add("Tom"); names.add("Peter"); names.add("Michael"); names.add("Johnson"); names.add("Vlissides"); } public static void test(List<String> set){ System.out.println(set); } public static void main(String args[]){ test(names); }
I do understand that:
1.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.
typesafe
2.List<?>: is an unbounded wildcard. But not sure what this is for? So if I change the test method to
test
public static void test(List<?> set){ System.out.println(set); }
it still works good. If you can explain the usage of this, I would greatly appreciate it.
EDIT: If I do this:
public static void test(List<?> set){ set.add(new Long(2)); //--> Error set.add("2"); //--> Error System.out.println(set); }
but if I change test to this:
public static void test(List<String> set){ set.add(new Long(2)); //--> Error set.add("2"); //--> Work System.out.println(set); }
3.List<T>:
public static void test(List<T> set){ //T cannot be resolved System.out.println(set); }
I guess I don't understand this syntax. I saw something like this, and it works:
public <T> T[] toArray(T[] a){ return a; }
Please explain this for me please? Sometimes, I see <T>, or <E>, or <U>, <T,E>. Are they all the same or do they represent something different?
<T>
<E>
<U>
<T,E>
4.List<Object>
public static void test(List<Object> set){ System.out.println(set); }
Then I got the error The method test(List<Object>) is not application for the argument List<String> for the below code. I am confused. I thought String was a subset of Object?
The method test(List<Object>) is not application for the argument List<String>
String
Object
public static void main(String args[]){ test(names); }
EDIT: If I try this
test((List<Object>)names);
then I got Cannot cast from List<String> to List<Object>
Cannot cast from List<String> to 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.
List<String>
public static void test(List<? extends Object> set) ...
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)
final
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>.
C
P
GenericType<C>
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/
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
FooHandler's
operateOnFoo
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
Let us talk about them in the context of Java history ;
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);
? 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>
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.
Theory
String[] can be cast to Object[]
String[]
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 test(List<Object> set) { List<Object> params = new List<>(); // This is a List<Object> params.addAll(set); // A String can be added to List<Object> params.add(new Long(2)); // A Long can be added to List<Object> System.out.println(params); } public static void main(String[] args) { List<String> argsList = Arrays.asList(args); test(argsList); // The object passed is a List<String> }
B. This code will give a runtime error:
public static void test(List<Object> set) { List<Object> params = set; // Surprise! Runtime error set.add(new Long(2)); // Also a runtime error System.out.println(set); } public static void main(String[] args) { List<String> argsList = Arrays.asList(args); test(argsList); }
C. This code will give a runtime error (java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]):
java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
public static void test(Object[] set) { Object[] params = set; // This is OK even at runtime params[0] = new Long(2); // Surprise! Runtime error System.out.println(params); } public static void main(String[] args) { test(args); }
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>.
set
main()
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!
test()
params
Long
(Hopefully I have everything right here, if my reasoning is wrong I'm sure the community will tell me.)
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().
참고URL : https://stackoverflow.com/questions/6231973/difference-between-list-list-listt-liste-and-listobject