不兼容的类型:Class <bar>无法转换为Class <cap#1>,其中CAP#1是一个新类型的变量

时间:2016-06-09 21:55:47

标签: java generics lambda

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

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

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

此外,请注意我已完成我的研究,这是错误与&#34;类似问题&#34;你可以在右侧面板上看到。这里有趣的部分是&#34;新型变量&#34;。

1 个答案:

答案 0 :(得分: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())