【问题标题】:Incompatible types: Class<Bar> cannot be converted to Class<CAP#1> where CAP#1 is a fresh-type variable不兼容的类型:Class<Bar> 不能转换为 Class<CAP#1>,其中 CAP#1 是新类型变量
【发布时间】:2016-06-09 21:55:47
【问题描述】:

我在编写一些代码时遇到了一些让我烦恼的事情。我在下面的代码示例中收集了两个示例。

cls1 行使用 lambda 表达式但不编译,而 cls2 行使用方法引用并编译。我知道,如果我使用的是非泛型对象,我没有任何问题,但在这里,我使用的是泛型,更具体地说,是通配符。

import java.lang.annotation.*;
import java.util.Optional;

public class MCVE {

  static class Foo {}
  static class Bar extends Foo {}

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.TYPE)
  static @interface Baz { Class<? extends Foo> value(); }

  @Baz(Bar.class)
  static class Quz {}

  // Lambda expression - doesn't compile
  Class<? extends Foo> cls1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
      .map(baz -> baz.value())
      .orElse(Bar.class);

  // Method reference - compiles
  Class<? extends Foo> cls2 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
      .map(Baz::value)
      .orElse(Bar.class);

}

在功能上,两条线都在做同样的事情。所以我只是不明白到底发生了什么导致使用 lambda 表达式失败而方法引用没有问题。

对于那些会问的人,编译时收到的错误如下:

MCVE.java:25: error: incompatible types: Class<Bar> cannot be converted to Class<CAP#1>
      .orElse(Bar.class);
                 ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Foo from capture of ? extends Foo
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

这是相当简洁的,并没有提供任何非常有用的信息。

另外,请注意,我已经完成了研究,这个错误与您在右侧面板上看到的“类似问题”中的错误不同。这里有趣的部分是“新鲜类型变量”。

【问题讨论】:

  • 总的来说:Java 的类型系统如今几乎是一个黑盒子,关于何时显式类型是必要和不必要的一般规则的方式并不多。
  • @Hosseini 谢谢你的链接。虽然它比我在“类似问题”中看到的更接近我所经历的,但它谈论的是分配——我想这在这里无关紧要——它没有解决为什么 lambda 不行的问题,但可以使用方法参考。
  • 在 Eclipse 中,我在这两种情况下都会出错。将map 的结果分配给Optional&lt;Class&lt;? extends Foo&gt;&gt; 类型的中间变量可以解决这两种情况。推断orElse 的参数显示Class&lt;? extends Foo&gt; 所以Bar.class 应该是适用的。奇怪...
  • @JornVernee 我也这样做了;但是,如果您手动编译,则 cls2 行不会出错。可能是 Eclipse 中的一个错误。

标签: java generics lambda


【解决方案1】:

假设你有 3 个类:

static class Foo {}
static class Bar extends Foo {}
static class Dem extends Foo {}

编译器会从 lambda 表达式中找到:

var x1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class));
// typeof x1 = Optional<Baz> 

var x2 = x1.map(baz -> baz.value())
// typeof x2 = Optional<Class<T>>, where <T extends Foo> - this is the black magic you suffer with
// E.g. it can be 
// a) Optional<Class<T=Foo>> or 
// b) Optional<Class<T=Bar>> or 
// c) Optional<Class<T=Dem>>

var x3 = x2.orElse(Bar.class);
// In case (a) and (b) - this code should work, in case (c) it should fail.
// Without additional explicit hint (conversion) compiler reports about this issue.

当您使用方法引用时 - 编译器会忽略描述的类型推断并使用原始 Baz 类型声明,所以

.map(Baz::value) // is identical to
.map(baz -> (Class<? extends Foo>) baz.value())

【讨论】:

  • 这个解释让我有些困惑,这就是为什么我还没有接受。你的 BarDem 类之间绝对没有区别,但你说它是不同的:“如果 (a) 和 (b) - 这个代码应该工作,如果 (c) 它应该失败。 "这太不一致了,我只是无法绕过这个来正确理解。也许你在某个地方有错字?
  • 当 T=Der 你有 typeof x2 = Optional>,所以你不能应用 orElse(Bar.class) 到它 - Der 和 Bar 是不兼容的。您不能将 der 对象转换为 Bar,反之亦然。这只是一个显示编译器问题的理论示例。
  • 好的,你的意图更清楚了,我明白你的意思。谢谢你的一切:)
猜你喜欢
  • 2017-02-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多