Question

Consider below method doSomething(List<Object>) which accepts List<Object> as parameter.

private void doSomething(List<Object> list) {
    // do something
}

Now consider below code snippet which tries to call doSomething() where I try to pass List<String> to doSomething()

List<Object> objectList;
List<String> stringList;

doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine 

Even below code throws compilation error

objectList = stringList;  // compilation error incompatible types

My question is why List<String> can not be passed to a method which accepts List<Object>?

Was it helpful?

Solution 3

This generic question in Java may look confusing to any one who is not very familiar with Generics as in first glance it looks like String is object so List<String> can be used where List<Object> is required but this is not true. It will result in compilation error.

It does make sense if you go one step further because List<Object> can store anything including String, Integer etc but List<String> can only store Strings.

Also have a look at: Why not inherit from List<T>?

OTHER TIPS

Because while String extends Object, List<String> does not extend List<Object>

Update:
In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>.

This is because collections do change. In your case, If List<String> was a subtype of List<Object>, then types other than String can be added to it when the list is referenced using its supertype, as follows:

List<String> stringList = new ArrayList<String>;
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object>
objectList.add(new Object());
String s = stringList.get(0);// attempt to assign an Object to a String :O

and the Java compiler has to prevent these cases.

More elaboration on this Java Tutorial page.

You could put an object of a wrong type into the list IF this worked:

private void doSomething(List<Object> list) {
    list.add(new Integer(123)); // Should be fine, it's an object
}

List<String> stringList = new ArrayList<String>();
doSomething(stringList); // If this worked....
String s = stringList.get(0); // ... you'd receive a ClassCastException here

The reason for these limitations have to do with variance considerations.

Take the following code:

public void doSomething(List<Object> objects)
{
  objects.add(new Object());
}

Expanding your example, you could try to do the following:

List<String> strings = new ArrayList<String>();
string.add("S1");

doSomething(strings);

for (String s : strings)
{
  System.out.println(s.length);
}

Hopefully it's obvious why this would break if the compiler allowed this code to be compiled (which it doesn't) - a ClassCastException would occur for the second item in the list when trying to cast the Object to a String.

To be able to pass generalized collection types, you need to do this:

public void doSomething(List<?> objects)
{
  for (Object obj : objects)
  {
    System.out.println(obj.toString);
  }
}

Again, the compiler is watching your back and were you to replace the System.out with objects.add(new Object()) the compiler wouldn't allow this because objects could have been created as List<String>.

For more background on Variance see the Wikipedia artical Covariance and contravariance

From Java Tutorials of Generics:

Let's test your understanding of generics. Is the following code snippet legal?

List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2 

Line 1 is certainly legal. The trickier part of the question is line 2. This boils down to the question: is a List of String a List of Object. Most people instinctively answer, "Sure!"

Well, take a look at the next few lines:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!

Here we've aliased ls and lo. Accessing ls, a list of String, through the alias lo, we can insert arbitrary objects into it. As a result ls does not hold just Strings anymore, and when we try and get something out of it, we get a rude surprise.

The Java compiler will prevent this from happening of course. Line 2 will cause a compile time error.

Source : Generics and Subtyping

It is sometimes expected that a List<Object> would be a supertype of a List<String> , because Object is a supertype of String .

This expectation stems from the fact that such a type relationship exists for arrays:

Object[] is a supertype of String[] , because Object is a supertype of String . (This type relationship is known as covariance .)

The super-subtype-relationship of the component types extends into the corresponding array types.

No such a type relationship exists for instantiations of generic types. (Parameterized types are not covariant.)

Check here for more details

If you are not sure what datatype it will take in you can make use of Generics in Java as follows

public static void doSomething(List<?> data) {

}

public static void main(String [] args) {
    List<Object> objectList = new ArrayList<Object>();
    List<String> stringList = new ArrayList<String>();
    doSomething(objectList);
    doSomething(stringList);
}

But while using the data, you will be required to specify proper data type as a Type Cast

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top