4

在编写处理表单数据的通用方法时,我遇到了以下(如我所见)意外行为。给定以下代码:

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);
   }  
}

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

42
class java.lang.Integer 

事实上,如果我在方法签名中添加另一个参数(如String b)并进行另一个分配T t2 = (T) b,程序会打印

42
class java.lang.String 

为什么t变量将其类型更改为Integer(无论如何,对类型 T 进行某种提升为 Object)?

欢迎对此行为进行任何解释

4

3 回答 3

3

(T) a未经检查的强制转换:由于类型擦除,运行时无法知道类型T是什么,因此它实际上无法检查是否a属于 type 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'。:-)

于 2012-08-30T21:04:10.247 回答
2

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

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

没有演员表。没有例外。

于 2012-08-30T23:52:26.567 回答
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。

很难理解您要做什么,因为您的示例代码什么都不做。

于 2012-08-31T00:30:33.667 回答