通用方法和通用类中的类型推断

问题描述:

无法理解Java中的泛型编程。通用方法和通用类中的类型推断

我看了一些关于它的教程,但仍然很困惑,尤其是当事情变得复杂时。

任何人都可以解释这个例子中发生了什么?

import java.util.Date; 

public class Test1 { 

    public static void main(String[] args) { 
     P<Cls> p = new P<>(); //<1> //I expect a ClassCastException here, but no. Why? //How does the type inference for class P<E> work? 
     System.out.println(p.name); //it prints 
//  System.out.println(p.name.getClass());//but this line throws ClassCastException //why here? why not line <1>? 

     test1(p);//it runs 
//  test2(p);//throws ClassCastException//What is going on in method test1&test2? 
       //How does the type inference for generic methods work in this case? 
     } 


    public static<T> void test1(P<? extends T> k){ 
     System.out.println(k.name.getClass()); 
    } 

    public static<T extends Cls> void test2(P<? extends T> k){ 
     System.out.println(k.name.getClass()); 
    } 
} 

class P<E>{ 
    E name = (E)new Date();//<2> 
} 

class Cls{} 

P<Cls> p = new P<>(); 

记住的Java由擦除,这意味着P构造并没有真正有任何想法E在运行时实现仿制药。 Java中的泛型纯粹是为了在编译时帮助开发人员。

这意味着,当你创建一个new P<>(),一个new Date()被创建,但实际上并没有转换为任何特定类型的,因为运行时不知道E什么。 E在运行时不存在,就类​​而言。 name只是一个Object参考,里面有一个Date。但是,只要运行时环境需要知道它是特定类型(在本例中为Cls),编写代码时使用name时,编译器会在不告诉您的情况下将该类型转换为该类型。

  • p.name.getClass()被编译为((Cls)p.name).getClass(),这将创建一个类转换异常。
  • test2()指定了非通用类型约束(extends Cls)。所以其致电p.name.getClass()同样被翻译为((Cls)p.name).getClass()

在另一方面:

  • System.out.println(p.name)实际上是相同的,因为System.out.println((Object)p.name)println一个非通用方法,它有一个object
  • test1(p.name)与此类似。由于运行时间实际上并不知道T是什么类型,因此在调用getClass()之前,它基本上将p.name作为Object

换句话说,这里是你的代码,因为它实际上是被编译:

class P{ 
    Object name = new Date(); 
} 

public static void main(String[] args) { 
    P p = new P(); 
    System.out.println(p.name); 
    System.out.println(((Cls)p.name).getClass()); 

    test1(p); 
    test2(p); 
} 


public static void test1(P k){ 
    System.out.println(k.name.getClass()); 
} 

public static void test2(P k){ 
    System.out.println(((Cls)k.name).getClass()); 
} 
+0

这说明*它发生时*,但它并不能解释为什么* *。为什么'p.name.getClass()'涉及到一个强制转换,而不是'p.name'?即为什么不把第一行编译为'System.out.println((Cls)p.name);'? – 2014-10-11 12:47:06

+0

@OliverCharlesworth:因为Java希望运行时知道编译器知道该对象的什么,以便调用它的方法。在这种情况下,编译器绘制的笔画比所需的更宽,因为'getClass()'恰好在所有'Objects'上都可用,但是如果它是在'Cls'上声明的方法,那么类转换就是必需的。另一方面,将一个对象传递给一个需要'Object'的方法不需要投射。 – StriplingWarrior 2014-10-11 12:56:42

+0

好的,我们所说的确实是编译器在某些情况下不依赖于上下文而使用cast。问题是,管理这个的规则是什么?想必它不是随机的。 (这是一个真正的问题;我无法弄清楚JLS中可能指定的地方等) – 2014-10-11 13:02:56