【问题标题】:Java generics type erasure of method parameters方法参数的 Java 泛型类型擦除
【发布时间】:2012-12-20 16:56:56
【问题描述】:

我从 Joshua Bloch 的一次 google I/O 益智演讲中得到了这个。这是代码

 public class Glommer<T> {
      String glom(Collection<?> obj){
         String result = "";
         for(Object o : obj){
              result += o;
         }
         return result;
      }

      int glom(List<Integer> ints){
           int result = 0;
           for(int i : ints){
                result += i;
           }
           return result;
       }

      public static void main(String args[]){
           List<String> strings = Arrays.asList("1", "2", "3");
           System.out.println(new Glommer().glom(strings));
      }

这个 main 方法抛出一个异常,因为 new Glommer 是一个原始类型,因此 Glommer 中的所有泛型都被删除了,所以它最终调用 int glom(List&lt;Integer&gt; ints) 而不是 String glom(Collection&lt;?&gt; obj)

我的问题是,即使我将glom() 称为new Glommer&lt;Integer&gt;().glom(strings),它也不应该调用int glom(List&lt;Integer&gt; ints) 方法,因为由于类型擦除,此方法实际上是int glom(List ints)strings 的类型为@987654331 @不是Collection?

【问题讨论】:

    标签: java generics types


    【解决方案1】:

    被调用的方法是在编译时定义的,而不是在运行时定义的。

    如果您在构造函数调用中添加参数,编译器将有足够的信息知道它必须调用第一个方法。否则,就好像泛型不存在一样。在这两种情况下,被调用的方法在运行时始终保持不变。

    编辑有些人似乎怀疑,所以这里有另一个例子:

    public class Test {
    
        private static void test(Object object) {
            System.out.println("Object method");
        }
    
        private static void test(Integer integer) {
            System.out.println("Integer method");
        }
    
        public static void main(String[] args) {
            Object object = Integer.valueOf(0);
            test(object);
        }
    
    }
    

    结果是:

    Object method
    

    您将 Integer 传递给您的方法,但编译器在编译时只知道它是一个对象。即使 Object 实际上是 Integer,jvm 也不会自动更改方法调用。

    【讨论】:

    • 我会说编译器确定要执行的方法的签名,但是要在运行时使用实际对象类型选择要执行的方法。
    • @tcb 我加了一个例子,试试看。
    • 我明白你想说什么,在这个例子中你是对的,但我的话是关于一个一般情况,你可以有一个带有重写方法的派生类。
    • @tcb 在通过动态调度(覆盖)选择方法的情况下,这仍然是相同的方法签名。在编译时选择要调用(重载)的方法签名。 +1
    【解决方案2】:

    您可以阅读有关Raw Types 的更多信息以完全了解它

    基本上,原始类型用于使用遗留代码,几乎原始类中的任何内容都将成为原始类型,在本例中是这两种方法。

    因此,当它是原始的时,有一个方法可以获取List 和一个Collection,因此称为List 一个,如果它不是原始的,则这些方法也不是原始的,它将调用@ 987654325@一个,因为它有额外的信息

    【讨论】:

      【解决方案3】:

      这是因为当 new Glommer() 在没有泛型的情况下调用 Generic&lt;Type&gt;() 时,所有类型匹配都会从类中删除。

      由于字符串变量是List,如果它没有任何通用&lt;Type&gt;,那么它将匹配glom(List ints)。类型检查直到稍后才会完成。

      当我们创建new Glommer&lt;AnyType&gt; 时,所有类型都保留在原位,因此当我们传递strings 变量时,它会进行类型检查。编译器现在可以检查它是否是 List&lt;Integer&gt;,因为不是,所以它被传递给 glom(Collection&lt;?&gt; obj) 方法。

      希望对您有所帮助,如有需要,请提出任何澄清!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多