Difference between List, List<?>, List<T>, List<E>, and List<Object>
Asked Answered
J

10

208

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
}

Why 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>?

Johannisberger answered 3/6, 2011 at 19:52 Comment(0)
F
88

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)

Fosterfosterage answered 3/6, 2011 at 20:1 Comment(10)
+1 for read only list on 2. I write some codes to demonstrate this in 2. tyvmJohannisberger
Why would people use List<Object> for?Johannisberger
It's a way to create a list that accepts any type of items, you seldom use it.Fosterfosterage
Actually I wouldn't think anyone would use it considering you can not have an identifier at all now.Similar
@if_zero_equals_one. That sounds wrong, what do you mean by that?Fosterfosterage
I mean you can now just write new ArrayList();Similar
@Similar Yes, but you would then get a compiler warning (it would warn and say that you are using raw types), and you never want to compile your code with warnings.Fosterfosterage
just don't try and sort it and you'll be fine lol. but seriously you're right.Similar
What's the defference between T and EAirport
@Airport T is type like in <T> T[] toArray(T[] a) (method in List interface) and E is element like in public interface List<E> extends Collection<E>. Though K and V also seem like E to mePseudo
M
29

For the last part: Although String is a subset of Object, but List<String> is not inherited from List<Object>.

Minacious answered 3/6, 2011 at 20:1 Comment(3)
Very good point; many assume that because class C inherits from class P, that List<C> also inherits from List<P>. As you pointed out this is not the case. The reason why was that if we could cast from List<String> to List<Object>, then we could put Objects into that list, thus violating the original contract of List<String> when attempting to retrieve an element.Cedeno
+1. Good point as well. So why would people use List<Object> for?Johannisberger
List<Object> can be used to store a list of objects of different classes.Minacious
D
21

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.

Doodlesack answered 3/6, 2011 at 20:1 Comment(4)
Why would people use List<Object> for?Johannisberger
I think "a list of something" is a better meaning for List<?> since the list is of some specific but unknown type. List<Object> would really be "a list of anything" since it can indeed contain anything.Bramlett
@Bramlett - I meant "anything" in the sense of "any one thing". But you're right; it means, "a list of something, but I'm not going to tell you what".Doodlesack
@Bramlett it was he meant why did you repeat his words? yes it was written with a little different words, but the meaning is the same...Heaton
C
14

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.

Cedeno answered 3/6, 2011 at 20:25 Comment(2)
tyvm, I upload both your comment and your answer, since it is a very good explanation. Now where and why would people use List<Object>?Johannisberger
Generally you should not use List<Object> as it sort of defeats the purpose of generics. However, there are cases where old code might have a List accepting different types, so you might want to retrofit the code to use type parameterization just to avoid compiler warnings for raw types. (But the functionality is unchanged)Cedeno
D
5

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

Dielle answered 3/6, 2011 at 20:25 Comment(0)
S
5

I would advise reading Java puzzlers. It explains inheritance, generics, abstractions, and wildcards in declarations quite well. http://www.javapuzzlers.com/

Similar answered 3/6, 2011 at 20:33 Comment(0)
E
5

Let us talk about them in the context of Java history ;

  1. 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);
  1. 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
  1. List< T> List< E>

Means generic Declaration at the premise of none T or E type in your project Lib.

  1. List< Object> means generic parameterization.
Equidistance answered 2/8, 2013 at 14:37 Comment(0)
S
5

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.)

Styliform answered 24/2, 2018 at 3:24 Comment(3)
I've tried your example A it it doesn't work: List<Object> cannot be applied to List<String>. You can't pass ArrayList<String> to a method that expects ArrayList<Object>.Villainy
Thanks, rather late in the date I have tweaked example A so that it does now work. The main change was to define argsList generically in main().Styliform
Example "B" is not true, thanks to type erasure, there is no way for the runtime to tell what the generic type of the list is without reflection. So that example runs just fine.Crumple
L
4

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.

Leicestershire answered 19/5, 2013 at 2:14 Comment(0)
D
0

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().

D answered 3/6, 2011 at 19:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.