Ошибка типа с использованием дженериков в java 8, но не java 7

У меня есть фрагмент кода, который компилируется под java 7, но не под java 8. Вот пример самодостаточного воспроизведения (я взял реальный код, который проявляет эту проблему, и пропустил все реализации):

import java.util.Iterator;

class ASTNode<T extends ASTNode> implements Iterable<T> {
  @Override public Iterator<T> iterator() { return null; }
}

class List<T extends ASTNode> extends ASTNode<T> {}

interface Function<F, T> {}

class Iterables {
  public static <F,T> Iterable<T> transform(
      Iterable<F> fromIterable, Function<? super F, ? extends T> function) { return null; }
}

class AstFunctions {
  public static <T extends ASTNode<?>> Function<T, String> prettyPrint() { return null; }
}

public class Main {
  public static void test() {
    List<? extends ASTNode<?>> list = null;
    Iterables.transform(list, AstFunctions.prettyPrint());
  }
}

Свидетель:

$ javac -version
javac 1.8.0_05

$ javac -source 1.7 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning

$ javac -source 1.8 Main.java
Main.java:23: error: method transform in class Iterables cannot be applied to given types;
    Iterables.transform(list, AstFunctions.prettyPrint());
             ^
  required: Iterable<F>,Function<? super F,? extends T#1>
  found: List<CAP#1>,Function<ASTNode<?>,String>
  reason: cannot infer type-variable(s) F,T#1,T#2
    (argument mismatch; Function<CAP#1,String> cannot be converted to Function<? super CAP#1,? extends String>)
  where F,T#1,T#2 are type-variables:
    F extends Object declared in method <F,T#1>transform(Iterable<F>,Function<? super F,? extends T#1>)
    T#1 extends Object declared in method <F,T#1>transform(Iterable<F>,Function<? super F,? extends T#1>)
    T#2 extends ASTNode<?> declared in method <T#2>prettyPrint()
  where CAP#1 is a fresh type-variable:
    CAP#1 extends ASTNode<?> from capture of ? extends ASTNode<?>
1 error

(Возможно, примечательно, что Eclipse, настроенный для совместимости с 1.8, не имеет проблем с этим кодом).

Является ли это ошибкой компилятора? Если нет, то при допущении, что мне разрешено изменять AstFunctions и Main (но не ASTNode, List, Function или Iterables), как я могу скомпилировать этот код? Я также хотел бы, если возможно, понять, какие изменения в системе типа Java 8 не скомпилируют этот код.

Ответ 1

UPDATE: см. другой ответ - это была ошибка в javac, которая была исправлена.


Похоже, что это не должно компилироваться и что Java 8 демонстрирует правильное поведение:

  • Iterables.transform ожидает Iterable<F> fromIterable и Function<? super F..., поэтому первым общим типом Function должен быть суперкласс универсального типа Iterable
  • в вашем основном типе Iterable F1 == ? extends ASTNode<?>, а первый тип функции, возвращаемый prettyPrint, - это F2 == T extends ASTNode<?>

Я не думаю, что есть способ доказать, что F2 является супертипом F1. Например, скажем, у вас есть:

class A1 extends ASTNode<A1> {}
class A2 extends ASTNode<A2> {}

и в основном:

List<? extends ASTNode<?>> list = new List<A1>(); //F1 = A1

Вы можете себе представить, что prettyPrint возвращает Function<A2, String> (т.е. F2 = A2), а A2 не является супер классом A1.

Итак, я считаю, что ошибка компиляции имеет смысл. Но я не буду пытаться доказать это на основе спецификаций, потому что это будет есть большую часть моего дня!

Ответ 2

Это не проблема в текущей версии компилятора на последнем javac 9. Любая очевидная ошибка должна быть проверена против этой версии перед любым другим рассмотрением.

Для последнего кода javac8 проверяемый репозиторий последний javac 8. Эта проблема уже исправлена ​​в этом репозитории. Проблема исправлена ​​с патчем для JDK-8033718