为什么泛型方法在赋值中更改了参数化类型?

时间:2012-08-30 20:59:19

标签: java generics

当编写通用方法来处理表单的数据时,我遇到了以下(我认为)未完成的行为。给出以下代码:

public class Test {
   public <T> void someGenericMethod(Integer a) {
      @SuppressWarnings("unchecked")
      T t = (T) a;
      System.out.println(t);
      System.out.println(t.getClass());
   }

   public static void main(String[] args) {
      Test test = new Test();
      test.<BigDecimal>someGenericMethod(42);
   }  
}

AFAIK,上面的代码应在行ClassCastException中生成T t = (T) a,因为main中的方法调用是将参数化类型设置为BigDecimal并从Integer转换为不允许使用BigDecimal,与我的预期相反,程序执行良好并打印以下内容:

42
class java.lang.Integer 

实际上,如果我在方法签名中添加另一个参数(如String b)并再生成另一个分配T t2 = (T) b,程序将打印

42
class java.lang.String 

为什么t变量将其类型更改为Integer(无论如何,在类型T对象上进行某种促销)?

欢迎任何有关此行为的解释

3 个答案:

答案 0 :(得分:3)

(T) a是一个未经检查的强制转换:由于type erasure,运行时无法知道T是什么类型,因此它实际上无法实现检查a是否属于T类型。

执行此操作时,编译器会发出警告;在您的情况下,您已通过撰写@SuppressWarnings("unchecked")来抑制该警告。


已编辑添加(以回应下方评论中的其他问题):

如果你想检查演员表,你可以这样写:

public class Test {
   public <T> void someGenericMethod(Class<T> clazz, Integer a) {
      T t = clazz.cast(a);
      System.out.println(t);
      System.out.println(t.getClass());
   }

   public static void main(String[] args) {
      Test test = new Test();
      // gives a ClassCastException at runtime:
      test.someGenericMethod(BigDecimal.class, 42);
   }
}

通过传入clazz,允许运行时检查强制转换;而且,更多的是,您允许编译器从方法参数推断出T,因此您不必再编写test.<BigDecimal>someGenericMethod

当然,调用该方法的代码仍然可以通过使用未经检查的强制转换来解决这个问题:

public static void main(String[] args) {
   Test test = new Test();
   Class clazz = Object.class;
   test.someGenericMethod((Class<BigDecimal>) clazz, 42);
}

但那是main的错,而不是someGenericMethod。 : - )

答案 1 :(得分:2)

编译时,上面的代码基本上变成了以下非泛型方法:

public void someGenericMethod(Integer a) {
   Object t = a;
   System.out.println(t);
   System.out.println(t.getClass());
}

没有演员。没有例外。

答案 2 :(得分:1)

您在方法签名中指定了一个类型参数,但从不使用它。

我想你想要这样的东西:

public class Test {
   public <T> void someGenericMethod(T someItem) {
      System.out.println(someItem);
      System.out.println(someItem.getClass());
   }
}

public static void main(String[] args) {
    Test test = new Test();
    BigDecimal bd = new BigDecimal(42);

    test.someGenericMethod(42);    // Integer
    test.someGenericMethod("42");  // String
    test.someGenericMethod(42L);   // Long
    test.someGenericMethod(bd);    // BigDecimal
}  

请注意,无需投射。

参数类型在方法签名中声明,并从参数中推断出来。

在你的代码中,你正在参数化方法调用(我从未见过)并传入一个int。

有点难以理解你正在尝试做什么,因为你的示例代码什么都不做。