Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is List<String> a subtype of List, but not of List<Object>? [duplicate]

Tags:

java

generics

Possible Duplicate:
Why is List<Number> not a sub-type of List<Object>?

Isn't String a subtype of Object in Java?

Then, how come I cannot pass an object of type List<String> into a function that accepts List<Object> as a parameter? I can, though, pass such an object into a function that accepts List as a parameter.

like image 942
dangerChihuahua007 Avatar asked Nov 29 '22 14:11

dangerChihuahua007


2 Answers

Isn't String a subtype of Object in Java?

Yes it is, but that doesn't make List<String> a subtype of List<Object>.

Consider this example:

    List<String> l1 = new ArrayList<String>();
    List<Object> l2 = l1;  // This is a compilation error in real Java
    l2.add(new Integer(42));
    String oops = l1.get(0);

If (hypothetically) List<String> was subtype of List<Object>, then you'd end up assigning a Integer to a String variable in the last statement. In short, we would have broken static type safety.


I can, though, pass such an object into a function that accepts List as a parameter.

Yes, that is true. But then you are dealing with raw types and you have to use explicit type casts when pulling objects out of the list. For example, the above would need to be rewritten as follows:

    List<String> l1 = new ArrayList<String>();
    List l2 = l1;
    l2.add(new Integer(42));
    String oops = (String) l1.get(0);

Note that the type-cast means we've had to replace static type-safety with runtime type-safety. (And, of course, in this case the typecast will fail at runtime because the instance has the wrong type.)

like image 139
Stephen C Avatar answered Dec 05 '22 05:12

Stephen C


In Java, List<S> is not a subtype of List<T> when S is a subtype of T. This rule provides type safety.

Let's say we allow a List<String> to be a subtype of List<Object>. Consider the following example:

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

Here you have a collection of T's, you want to add all items from another collection. You can't call this method with a Collection<S> for an S subtype of T. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.

To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.

like image 20
aromero Avatar answered Dec 05 '22 05:12

aromero