3

I don't really know how to explain the problem I have. Below is an example of a few objects I have.

Lets say I have classes

public class Foo{}
public class FooBar extends Foo{}

All of that is fine, however if I take a list and say I want it to be full of Foo objects, I cannot provide it a FooBar object. Why is this?

Example:

List<Foo> fooList = Arrays.asList(new FooBar());

If FooBar extends Foo, why cant it be cast back to Foo?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
miversen33
  • 573
  • 5
  • 19
  • declare list as List extends Foo> fooList = Arrays.asList(new FooBar()); . Please go through Java Generics, that should in understanding it. – akshaya pandey Dec 06 '17 at 05:25
  • 3
    [This works fine,](https://ideone.com/pmlMtJ) other than the missing constructor parentheses. Please provide a [mcve]. – shmosel Dec 06 '17 at 05:25
  • It compiles for me, but if I add `-source 1.7` to the `javac` command line, it fails on `incompatible types: List cannot be converted to List`. So I think this was something where they changed the rules in Java 8. Unfortunately, Android (it looks like you had an "Android" tag and it got deleted) is behind--are they fully up to 1.7 yet? – ajb Dec 06 '17 at 05:27
  • 5
    By the way, in the future, please give a specific error message or behavior, instead of saying "It doesn't work" or "It doesn't compile" or "I can't do this". The compiler's error messages help explain exactly what the error is, and even if you don't understand what they mean, those of us reading the question often do, and it helps us answer your questions. – ajb Dec 06 '17 at 05:30

2 Answers2

5

As already mentioned in comments Arrays.asList(T...) returns List<T>. Since you passed FooBar instance to that method type inference from Java 7 used FooBar type as T representation which means that this method returns List<FooBar> which can't be assigned to List<Foo> (you can read more about it at: Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?)

In Java 8 type inference was improved to make lambdas more useful. Now T infers type not only from parameters, but also from target - reference variable which should hold result. This allows T to infer Foo from List<Foo> fooList rather than new FooBar() argument.

To solve this problem in earlier versions like in your case Java 7 you can explicitly set generic type for generic method asList like

List<Foo> fooList = Arrays.<Foo>asList(new FooBar());
//                         ^^^^^ -- set generic types before method name

More info at: https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html (especially "Target Types" section)

Pshemo
  • 122,468
  • 25
  • 185
  • 269
3

Arrays.asList(new FooBar()) returns a List<FooBar>, which cannot be assigned to a List<Foo>. If it could be assigned to that, then you could create a List<FooBar>, assign it to a List<Foo>, and then add Foo to a list that must contain FooBar, which is clearly an error.

Instead, you need:

List<Foo> fooList = new ArrayList<>();
fooList.add(new FooBar());
AJNeufeld
  • 8,526
  • 1
  • 25
  • 44
  • 3
    *`Arrays.asList(new FooBar())` returns a `List`*. Not if `T` is `Foo`, which it is. – shmosel Dec 06 '17 at 05:27
  • This compiles fine in Java 8. Of course, that doesn't help Android users. I don't know the exact rule change, but I think in Java 8, the language uses the type of the left side of `=` to help it interpret the meaning of the right side, while in Java 7 it only looks at the expression on the right side and is not cognizant of what's on the left. I think the change was necessary to get lambdas to work. – ajb Dec 06 '17 at 05:30
  • @shmosel : `Arrays.asList` is declared as ` List asList​(T... a)`, so if you pass it `T = FooBar`, why would it infer `T = Foo`? – AJNeufeld Dec 06 '17 at 05:31
  • From the assignment to `List`. But don't take my word for it, try it yourself. – shmosel Dec 06 '17 at 05:33
  • @shmosel : Interesting. `List foobarList = Arrays.asList(new FooBar()); fooList = foobarList;` does not compile, as expected. But Java 8 does seem to infer the correct `T` if assigning directly to `fooList`. Learn a new thing every day! – AJNeufeld Dec 06 '17 at 05:40