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



在编写处理表单数据的通用方法时,我遇到了以下(在我看来)未表达的行为。给定以下代码:

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

42
class java.lang.Integer 

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

42
class java.lang.String 

为什么t变量将其类型更改为Integer(无论如何,这是在某种程度上将类型T提升为Object)?

对此行为的任何解释都是受欢迎的

(T) a是一个未检查的强制转换:由于类型擦除,运行时无法知道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的错。:-)

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

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

没有演员阵容。也不例外。

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

我想你想要这样的东西:

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.

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

相关内容

最新更新