1

I made a static class method (hereby called a "function") f that takes a list of Callable<String>s:

import java.util.concurrent.*;
import java.util.*;
class A {
  static void f(List<Callable<String>> l) {}
  static <T> List<T> s(T t) {
    LinkedList<T> l = new LinkedList<T>();
    l.add(t);
    return l;
  }
  public static void main(String[] _) {
    f(s(new Callable<String>() {
      public String call() throws Exception {
        return "HI";
      }
    }));
  }
}

I made another function s that wraps a single element in a list, so I can make a list with a single Callable in it to test f.

It fails to compile unless i extract the Callable into a variable. Why?

In eclipse, it gets this error:

The method f(List<Callable<String>>) in the type A is not applicable for the arguments (List<new Callable<String>(){}>)

which is odd. It seems be saying an expression (which yields a value) is a type. Using javac it gets a different error:

A.java:11: error: method f in class A cannot be applied to given types;
    f(s(new Callable<String>() {
    ^
  required: List<Callable<String>>
  found: List<<anonymous Callable<String>>>
  reason: actual argument List<<anonymous Callable<String>>> cannot be converted to List<Callable<String>> by method invocation conversion
1 error
Dog
  • 7,707
  • 8
  • 40
  • 74
  • 1
    This compiles for me... Copy/pasted code into Eclipse, works fine. – awksp May 22 '14 at 04:02
  • However, it doesn't in Java 7. Appears to be a limitation on type inference in Java 7. Worked in Java 1.8.0_05. – awksp May 22 '14 at 04:05

2 Answers2

3

Change the signature of f to have an upper bound to Callable.

static void f(List<? extends Callable<String>> l) {}

This is needed because:

  1. An anonymous class is actually a subclass of the declared interface/superclass
  2. a List<subclass> is not assignable to List<superclass>
Bohemian
  • 412,405
  • 93
  • 575
  • 722
2

The error message is a bit misleading. I'm guessing it is that way because the compiler has yet to give a name to the anonymous type (or chooses not to use it). But basically, a List<SomeSubTypeOfCallable> is not a subtype of List<Callable> and therefore is not a valid argument for a method expecting the latter.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • But `Callable` is an interface How could anything that implements `Callable` not be `Callable`? If what you said is true, why does it compile when I extract the anonymous class expression to a local variable and pass that local variable to `s` instead? – Dog May 22 '14 at 04:14
  • @Dog I think the issue is that the compiler's type inference appears to be going to the most specific type available (subclass of `Callable`), and not realizing that by walking up the type hierarchy it could find a type that would allow your method as written to work. Java 8's compiler is smart enough to do this, though, and so emits no error. It also could be some bug in the way anonymous classes are handled. I'm not sure. – awksp May 22 '14 at 04:17
  • @Dog Read [this](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p). In Java 8 type inference was improved. In this case, because of the invocation context, `T` in `s` will be inferred as `Callable`, a `Callable` subclass (anonymous) will be acceptable as an argument and the method will be bound as returning a `List` which is then a valid argument to `f`. – Sotirios Delimanolis May 22 '14 at 04:27
  • I know about that but I don't see how it applies here. – Dog May 22 '14 at 04:34
  • @Dog The type of the expression `new Callable() {...}` is actually `ASubTypeOfCallable`. Used with the method `s`, you therefore bind `ASubTypeOfCallable` to the type variable `T`. The method therefore has a return type of `List>` which is not a subtype of `List>` and therefore cannot be used as an argument to `f`. – Sotirios Delimanolis May 22 '14 at 04:40