Incompatible types: Class<Bar> cannot be converted to Class<CAP#1> where CAP#1 is a fresh-type variable

回眸只為那壹抹淺笑 提交于 2021-02-10 13:34:31

问题


I encountered something that bugs me when I wrote some code. I gathered the two examples in the code sample below.

The cls1 line uses a lambda expression but doesn't compile, while the cls2 line uses a method references and compiles. I know that if I'm using non generic objects, I have no issues there, but here, I'm using generics, and, more specifically, wildcards.

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);

}

In functionality, both lines are doing the same. So I just don't understand what's going on under the roof that makes the use of a lambda expression fail while a method reference has no issue.

For those who'll ask, the error received while compiling is the following:

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

This is rather laconic and doesn't provide any extremely useful piece of information.

Also, please note that I've done my research and that this is error is not the same as in the "Similar Questions" you can see on the right panel. The interesting part here is "fresh-type variable".


回答1:


Imagine you have 3 classes:

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

Compiler will find from lambda expression:

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.

When you use method reference - compiler ignores described type inference and uses original Baz type declaration, so

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


来源:https://stackoverflow.com/questions/37736824/incompatible-types-classbar-cannot-be-converted-to-classcap1-where-cap1-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!