泛型 - 编译器不一致[jdk 1.8.0_162]

n24*_*47s 6 java generics compiler-errors java-8

我遇到了泛型问题让我对编译器实际处理泛型类型的方式感到困惑.考虑以下:

// simple interface to make it a MCVE
static interface A<F, S> {
    public F getF();    
    public S getS();
}

static <V, S> Comparator<A<V, S>> wrap(Comparator<S> c) {
    return (L, R) -> c.compare(L.getS(), R.getS());
}
Run Code Online (Sandbox Code Playgroud)

以下将不会编译,因为两个泛型类型都减少为Object调用时thenComparing:

Comparator<A<String, Integer>> c = wrap((L, R) -> Integer.compare(L, R))
    .thenComparing(wrap((L, R) -> Integer.compare(L, R)));
Run Code Online (Sandbox Code Playgroud)

但是如果我像下面的例子那样将它们分解,那么一切都正确地编译(并运行):

Comparator<A<String, Integer>> c = wrap((L, R) -> Integer.compare(L, R));
c = c.thenComparing(wrap((L, R) -> Integer.compare(L, R)));
Run Code Online (Sandbox Code Playgroud)

所以问题是:这里发生了什么?我怀疑这是由于编译器的一些奇怪行为而不是预期的语言规范?或者我错过了一些明显的东西?

Sha*_*dov 5

第二次尝试正确编译,因为您自己指定了变量的类型,告诉编译器它是什么,因为编译器没有足够的信息来解决它.

看看这个简化的例子,它来自vavr(顺便说一下).有一个Try<T>类表示某些操作的结果.通用参数T是该结果的类型.有一个静态工厂可以立即创建失败,这意味着我们没有结果,但泛型参数仍然存在:

static <T> Try<T> failure(Throwable exception) {
    return new Try.Failure(exception);
}
Run Code Online (Sandbox Code Playgroud)

这里T来自哪里?用法如下:

public Try<WeakHashMap> method() {
  return Try.failure(new IllegalArgumentException("Some message"));
}
Run Code Online (Sandbox Code Playgroud)

Try<WeakHashMap>是我的选择,而不是编译器,你实际上可以在那里放任何你想要的东西,因为你正在选择类型.

在你的例子中同样的事情,只有Comparator泛型参数String,因为你指定了它并且编译器同意它(比如Try<WeakHashMap>).当你添加一个链式调用时,你强迫编译器推断出类型本身,它是Object,因为它可能是另一种类型?

你还能做什么(注意Testing.<String, Integer>wrap):

public class Testing {
  static interface A<F, S> {
    public F getF();
    public S getS();
  }

  static <V, S> Comparator<A<V, S>> wrap(Comparator<S> c) {
    return (L, R) -> c.compare(L.getS(), R.getS());
  }

  public static void main(String[] args) {
    Comparator<A<String, Integer>> comp = Testing.<String, Integer>wrap((L, R) -> Integer.compare(L, R))
      .thenComparing(wrap((L, R) -> Integer.compare(L, R)));
  }
}
Run Code Online (Sandbox Code Playgroud)