【问题标题】:Generic method invocation with <T>使用 <T> 的通用方法调用
【发布时间】:2013-01-28 03:21:38
【问题描述】:

我在理解这种泛型方法调用时遇到了问题:

object = ObjectGenerator.<T> getObject(objectName);

以下是上述情况的上下文:

class GenClass<T> {

   private T object;

   // ... some code

   public void initObject(String objectName) {
      object = ObjectGenerator.<T> getObject(objectName);
   }
}

class ObjectGenerator {

   public static <T extends Object> T getObject(String name) {
      // some code
      return someObject;
   }
}

问题是&lt;T&gt;getObject(objectName) 调用之前扮演什么角色?

【问题讨论】:

  • +1 因为我以前没见过。
  • 我不是在问它的意义,而只是在询问一行代码。我可以将方法签名更改为&lt;T extends Object&gt; T getObject(String name, Class&lt;T&gt; clazz) - 没关系。只有用&lt;T&gt; 调用该方法的这一行让我感到困惑

标签: java generics jls


【解决方案1】:

注意:在您给出的具体示例中,ObjectGenerator.getObject(objectName); 应该可以正常编译。

在某些情况下,类型推断机制无法解决以下事实:

T object;
object = ObjectGenerator.getObject(objectName);

返回的类型应该是T。在这种情况下,您需要通过显式指示您期望的返回类型来给编译器一点帮助。

这是一个人为的示例,您需要显式指定泛型类型:

class Example {
    public static <T> List<T> m1() {
        return m2(Arrays.<T> asList()); //Arrays.asList() would not compile
    }
    public static <T> List<T> m2(List<T> l) {
        return l;
    }
}

【讨论】:

  • 在什么情况下“类型推断机制”无法知道返回的类型应该是什么?
  • @Jeff 我添加了一个示例 - 发生这种情况的确切情况取决于应用 type inference rules 的结果,在 JLS 的 pdf 版本中大约有 12 页长,所以不完全一定要诚实。最后我看待它的方式是:要么它编译没有显式类型,我很高兴,要么它没有,我只是添加它......
  • 是的,我明白了!这对我来说是一个很好的解释,从你的链接中查看这个规范,我想还有更多令人讨厌的情况要发现! :)
  • 我不太理解您如何证明省略类型规范的合理性。也许我错过了一些导致我误解的东西。我只是提供了我自己的答案。你能看一下并分享你的想法吗?
  • 泛型类型恰好是该示例中的返回类型。在我答案底部的代码中不是。
【解决方案2】:

object 可以是 T 的孩子。

当然getObject应该更好地定义:

public static <T> T getObject(Class<T> objectClass, String name) {
   return objectClass.getConstructor(String.class).newInstance(name);
   //return objectClass.getConstructor().newInstance();
}

否则,由于所谓的类型擦除,不可能进行类型安全的构造。

【讨论】:

    【解决方案3】:

    我在这里找到了一些东西:https://stackoverflow.com/a/338906/443427 它可能会对你有所帮助。

    从我读到的可能代表编译器必须使用的泛型来计算返回的值类型,以传递泛型转发

    请注意,这也可以正常工作(检查 ObjectGenerator 与 T 不同):

    public class ObjectGenerator<X> {
        public static <T extends Object> Set<T> getObject(String name) {
        // some code
        return null;
        }
    }
    

    【讨论】:

      【解决方案4】:

      我不完全同意接受的答案,其中说:

      在这种情况下,你需要显式地给编译器一点帮助 指示您期望的返回类型。

      这对我来说听起来不对。据我了解泛型方法和类型推断,方括号中提供的类型并不直接指示泛型方法的返回类型。相反,T 类型可以是返回类型、参数类型、与泛型方法关联的局部变量类型。

      实际上,由于类型推断机制,我们在大多数情况下(不仅在某些情况下)不需要指定类型参数T。在您的示例中,可以安全地从方法调用 ObjectGenerator.&lt;T&gt; getObject(objectName) 中省略 &lt;T&gt; ,就像在大多数其他情况下一样。这是因为泛型方法的类型T 可以很容易地从分配或返回结果的类型中推断出来。换句话说,由于您在方法调用之前声明了private T object,因此类型T 将被成功推断为T

      a definitive tutorial 的以下声明可以支持我的主张:

      类型推断是 Java 编译器查看每个方法的能力 调用和相应的声明来确定类型 使调用适用的参数(或参数)。这 推理算法确定参数的类型,如果 可用,分配或返回结果的类型。 最后,推理算法试图找到最具体的类型 它适用于所有参数。

      关于推理如何工作的两个示例:

      static <T> T pick(T a1, T a2) { return a2; }
      Serializable s = pick("d", new ArrayList<String>());
      

      T 类型根据声明的受理人类型推断为Serializable

      public static <U> void addBox(U u, java.util.List<Box<U>> boxes) {}
      BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
      

      U 类型根据传递参数的类型推断为Integer(即Integer.valueOf(10) 的类型为Integer)。因此,&lt;Integer&gt; 可以安全地从上面的方法调用中省略。

      总而言之,除非我们无法从泛型方法的实参类型或结果被分配或返回的类型(调用方法时)推断出泛型方法的类型参数,否则我们可以安全地省略类型说明对在方法调用之前。

      【讨论】:

        【解决方案5】:
         object = ObjectGenerator.getObject(objectName);
        

        始终返回 Object 的对象,因为您调用的是静态方法。为了在代码中显式获取您用于 T 的通用对象,

         object = ObjectGenerator.<T> getObject(objectName); 
        

        &lt;T&gt; 用于静态上下文。对于非静态调用,不需要它。

        【讨论】:

        • -1 静态上下文与显式提供类型参数无关。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        • 2023-03-16
        • 2020-03-06
        • 1970-01-01
        • 1970-01-01
        • 2013-10-26
        • 1970-01-01
        相关资源
        最近更新 更多