Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 method reference type inference

I'm confused by a bit of Java 8's type inference. The following code:

private static <T> Function<Iterable<? extends T>, Iterator<? extends T>>
    toIterator() {
  return Iterable<? extends T>::iterator;
}

breaks with the compile error

error: incompatible types: invalid method reference
    return Iterable<? extends T>::iterator;
           ^
    method iterator in interface Iterable<T#2> cannot be applied to given types
      required: no arguments
      found: Iterable<? extends T#1>
      reason: actual and formal argument lists differ in length
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>toIterator()
    T#2 extends Object declared in interface Iterable

whereas removing the explicit generic

private static <T> Function<Iterable<? extends T>, Iterator<? extends T>>
    toIterator() {
  return Iterable::iterator;
}

works, as does the old-school anonymous inner class

private static <T> Function<Iterable<? extends T>, Iterator<? extends T>>
    toIterator() {
  return new Function<Iterable<? extends T>, Iterator<? extends T>>() {
    @Override
    public Iterator<? extends T> apply(Iterable<? extends T> iterable) {
      return iterable.iterator();
    }
  };
}

Can anyone suggest what might be going on here?

like image 681
Louis Wasserman Avatar asked Aug 13 '14 16:08

Louis Wasserman


1 Answers

Using <? extends T> is wrong because for Generic invocations with explicit type arguments you have to specify complete types, not wildcard types.

If you simply use

private static <T> Function<Iterable<? extends T>, Iterator<? extends T>>
    toIterator() {
  return Iterable::iterator;
}

Java will infer the type for you.


A solution would be:

private static <T> Function<Iterable<T>, Iterator<? extends T>> toIterator() {
  return Iterable<T>::iterator;
}

though it is not clear what the advantage of widening the Function’s return type to ? extends T shall be. For practical uses,

private static <T> Function<Iterable<T>, Iterator<T>> toIterator() {
  return Iterable<T>::iterator;
}

will be the most useful method signature. For this, the inferring Iterable::iterator still works as well.


Regarding the compiler error message, this seems just to be a bug in the error reporting which applies always when a method reference to a non-static method has a type mismatch. It can be reproduced even by the simple statement:

Consumer<Object> c=String::getClass;

which produces the error message:

error: incompatible types: invalid method reference
Consumer<Object> c=String::getClass;
                   ^
method getClass in class Object cannot be applied to given types
  required: no arguments
  found: Object
  reason: actual and formal argument lists differ in length

Note that references to static methods get the right error message:

Consumer<Object> c=Class::forName;

produces:

error: incompatible types: invalid method reference
Consumer<Object> c=Class::forName;
                   ^
incompatible types: Object cannot be converted to String
like image 159
Holger Avatar answered Oct 02 '22 11:10

Holger