在返回类型中推断通配符泛型 [英] Inferred wildcard generics in return type

查看:87
本文介绍了在返回类型中推断通配符泛型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java常常可以根据参数推断泛型(甚至在返回类型上,与C#相比)。

案例:我有一个通用类 Pair< T1,T2> ,它只存储一对值并可按以下方式使用:

  Pair< String,String> pair = Pair.of(Hello,World); 

的方法看起来就像这样:

  public static< T1,T2>对< T1,T2> (T1第一,T2秒){
返回新对< T1,T2>(第一,第二);
}

非常好。然而,这不再适用于下面的用例,它需要通配符:

  Pair< Class<?>,String> ; pair = Pair.of((Class <?> List.class,hello); 

(注意显式转换使 List.class

代码失败,出现以下错误(由Eclipse提供):


类型不匹配:无法从 TestClass.Pair< Class< capture#1-of?>,String> 转换为 TestClass.Pair< Class<?> String>


然而,显式调用构造函数仍然有效如预期:

  Pair< Class<?>,String>对(pair)= 
new Pair< Class<>,String>((Class<> List.class,hello);

有人可以解释这种行为吗?它是否由设计?这是 吗?我做错了什么,或者我偶然发现了编译器中的设计/错误?



疯狂的猜测:capture#1 of?暗示通配符由编译器即时填充,使类型成为 Class< List> ,并因此失败转换(从 Pair< Class <?>,String> to Pair< Class< List>,String> )。这是正确的吗?有没有办法解决这个问题?






为了完整起见,下面是<$ c的简化版本
$ b

  public final class Pair< T1,T2> $ c> Pair  class: {
public final T1 first;
public final T2秒;

public Pair(T1 first,T2 second){
this.first = first;
this.second = second;
}

public static< T1,T2>对< T1,T2> (T1第一,T2秒){
返回新对< T1,T2>(第一,第二);
}
}


解决方案

因为构造函数的工作原理是你明确指定了类型参数。如果你这样做,静态方法也可以工作:

  Pair< Class<?>,String> (List.class,hello);< Class<> 

当然,您首先使用静态方法的原因可能仅仅是获取(这根本不适用于构造函数)。



这里的问题(如你所建议的)是编译器正在执行捕捉转换。我相信这是由于 [JLS]第15.12.2.6节



  • 选择的结果类型方法确定如下:


    • 如果被调用的方法声明为返回类型为void,则
      则结果为void。 li>
    • 否则,如果需要对
      方法进行未经检查的转换,则
      结果类型是
      的擦除(第4.6节),方法的声明返回如果被调用的方法是通用的,那么对于1in,让
      Fi为
      的正式类型参数,让Ai为为方法
      调用推断的实际类型
      参数,并且让R为被调用的
      方法的声明
      返回类型。通过将捕获转换
      (§5.1.10)应用于R [F1:= A1,...,Fn:=
      An],获得结果类型

    • 否则,结果类型是通过在方法声明中将
      转换(第5.1.10节)应用于给定
      的类型获得的。

    • 如果你真的想要推断,一种可能的解决方法是做这样的事情:

        Pair <? extends Class<?>,String> pair = Pair.of(List.class,hello); 

      变量 pair 将有更宽的类型,而且它确实意味着在变量的类型名称中输入了更多内容,但至少不需要在方法调用中投入。


      Java can often infer generics based on the arguments (and even on the return type, in contrast to e.g. C#).

      Case in point: I've got a generic class Pair<T1, T2> which just stores a pair of values and can be used in the following way:

      Pair<String, String> pair = Pair.of("Hello", "World");
      

      The method of looks just like this:

      public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
          return new Pair<T1, T2>(first, second);
      }
      

      Very nice. However, this no longer works for the following use-case, which requires wildcards:

      Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello");
      

      (Notice the explicit cast to make List.class the correct type.)

      The code fails with the following error (provided by Eclipse):

      Type mismatch: cannot convert from TestClass.Pair<Class<capture#1-of ?>,String> to TestClass.Pair<Class<?>,String>

      However, explicitly calling the constructor still works as expected:

      Pair<Class<?>, String> pair =
          new Pair<Class<?>, String>((Class<?>) List.class, "hello");
      

      Can someone explain this behaviour? Is it by design? Is it wanted? Am I doing something wrong or did I stumble upon a flaw in the design / bug in the compiler?

      Wild guess: the "capture#1-of ?" somehow seems to imply that the wildcard is filled in by the compiler on the fly, making the type a Class<List>, and thus failing the conversion (from Pair<Class<?>, String> to Pair<Class<List>, String>). Is this right? Is there a way to work around this?


      For completeness’ sake, here is a simplified version of the Pair class:

      public final class Pair<T1, T2> {
          public final T1 first;
          public final T2 second;
      
          public Pair(T1 first, T2 second) {
              this.first = first;
              this.second = second;
          }
      
          public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
              return new Pair<T1, T2>(first, second);
          }
      }
      

      解决方案

      The reason the constructor works is that you're explicitly specifying the type parameters. The static method also will work if you do that:

      Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello");
      

      Of course, the whole reason you have a static method in the first place is probably just to get the type inference (which doesn't work with constructors at all).

      The problem here (as you suggested) is that the compiler is performing capture conversion. I believe this is as a result of [§15.12.2.6 of the JLS]:

      • The result type of the chosen method is determined as follows:
        • If the method being invoked is declared with a return type of void, then the result is void.
        • Otherwise, if unchecked conversion was necessary for the method to be applicable then the result type is the erasure (§4.6) of the method's declared return type.
        • Otherwise, if the method being invoked is generic, then for 1in, let Fi be the formal type parameters of the method, let Ai be the actual type arguments inferred for the method invocation, and let R be the declared return type of the method being invoked. The result type is obtained by applying capture conversion (§5.1.10) to R[F1 := A1, ..., Fn := An].
        • Otherwise, the result type is obtained by applying capture conversion (§5.1.10) to the type given in the method declaration.

      If you really want the inference, one possible workaround is to do something like this:

      Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello");
      

      The variable pair will have a wider type, and it does mean a bit more typing in the variable's type name, but at least you don't need to cast in the method call anymore.

      这篇关于在返回类型中推断通配符泛型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆