user1842039
user1842039

Reputation: 69

List vs List<Object>?

Please explain what are the differences between List - raw type and List<Object>.

The below code gives run time error:

public static void main(String[] args) {
    List<String> strings = new ArrayList<String>();
    unsafeAdd(strings, new Integer(42));
    String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
    list.add(o);
}

And this gives compile time error:

public static void main(String[] args) {
    List<String> strings = new ArrayList<String>();
    unsafeAdd(strings, new Integer(42));
    String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List<Object> list, Object o) {
    list.add(o);
}

Upvotes: 3

Views: 371

Answers (5)

Honza Zidek
Honza Zidek

Reputation: 19936

However it looks the same, it is not.

List (raw type) does not care what you insert into it.

List<String> is not compatible with List<Object>. You may do this:

String s = "Hello";
Object o = s;

but you must not do this:

List<String> ls = ...;
List<Object> lo = ls; // Compilation error!

Your example is a good illustration why the Java language does not allow it. By allowing such an assignment a malicious method would be able to put anything into a list which a client consider as a list of Strings.

Consider the following code:

public void method changeObject(Object o) {
    o = 42;
}
public void method changeList(List<Object> lo) {
    lo.add(42);
}
...
String str = "Hello";
changeObject(str);
// Here it is still safe to use str, its value has not been modified
String str2 = str; // OK

List<String> list = new ArrayList<>();
changeList(list); // it is not allowed, but let's assume it is
// here the list variable would violate its contract - everybody has a right 
// to expect it is a list of Strings
String str3 = list.get(0); // Ouch! :(

Upvotes: 0

A. A. Vybegallo
A. A. Vybegallo

Reputation: 246

Java has not inheritance for parametric types. So List<Integer> is not a subclass of List<Object>, then you can't use List<Integer> or List<String> as parameter for the unsafeAdd method. But you can write:

private static <T> void unsafeAdd(List<T> list, T o) {
     list.add(o);
}

and safelly call it:

List<String> strings = ...
unsafeAdd(string, "42");

and get error while:

List<String> strings = ...
unsafeAdd(strings, 42); // error!

You can see more information in the Oracle Generics Tutorial, Generics, Inheritance, and Subtypes

Upvotes: 2

Thomas
Thomas

Reputation: 88707

As Peter already said, in the first example you're telling the compiler to use raw types and thus not to perform any checks on the list. Thus it will allow you to add any object to the passed list, even if it is defined to just allow for strings.

In the second example you tell the compiler that it should assume the list to allow any object, thus the add operation would compile. However, passing a List<String> as a List<Object> is not allowed, since the list of strings has more specific restrictions than the contents just being objects and hence the compiler knows that this is unsafe and error-prone.

If you'd define the parameter to be List<? extends Object> list instead, the compiler would accept passing a List<String> since you tell it that the minimum requirement is that the list must accept objects, but it could also impose harder constraints. However, the add operation wouldn't compile now, since the compiler doesn't know if there are harder constraints and if so what these constraints are. Hence it can't assure that the add operation is safe and refuses to compile that line.

Upvotes: 0

amorfis
amorfis

Reputation: 15770

In the first case you pass unparametrized List to unsafeAdd, so compiler has no way to figure out something is wrong.

Passing List<String> to method which expects List is ok. Adding object to List is ok.

In the second case, you are passing List<String> to method which expects List<Object> - and that's not ok. Because this way you are implicitely allowing to add non-String to List<String> - compiler can figure it out in this case and raises an error.

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533492

In the second case, you are doing something the compiler can workout is not safe. In the first case, you are using raw types so the compiler doesn't perform the same checks.

Upvotes: 7

Related Questions