Java泛型与类<T>

9

所以我有一张地图:

Map<String, Class> format = new HashMap<String, Class>();

我会像这样添加元素:

format.put("Vendor Number", Integer.class);
format.put("Vendor Dispatch", Date.class); 
....

我有一个通用方法如下:

我有一个通用方法如下:

public static <T> T verifyType(String name, Class<T> type) {
    if (type == Integer.class) {
        return type.cast(new Integer(Integer.parseInt(name)));
    }
             ......
    return null;
}

现在这段代码完美运行,没有编译问题:

Integer i = verifyType("100",Integer.class);

但是,当我尝试这样做时:
    Integer i = verifyType("100",format.get("Vendor Number"));

OR 

    Class type = Integer.class
    Integer i = verifyType("100",type);

编译器显示以下警告: 类型安全性:未经检查的调用verifyType(String,Class)通用方法verifyType(String,Class)
这让我感到困惑...请帮忙解决。
6个回答

8

更改:

Class type = Integer.class
Integer i = verifyType("100",type);

to

Class<Integer> type = Integer.class
Integer i = verifyType("100",type);

如果只将类型声明为“Class”,您将失去通用参数和verifyType()方法无法推断类,因此会出现未经检查的警告。

这个问题:

Map<String, Class> format = new HashMap<String, Class>();
format.put("Vendor Number", Integer.class);
format.put("Vendor Dispatch", Date.class);
Integer i = verifyType("100",format.get("Vendor Number"));

由于类型擦除,这个问题实际上无法解决。编译器无法根据在运行时已经消失的通用参数推断类型。这是因为Java泛型只不过是一些用于转换的花招而已。


这可以工作,但显然,如果我从声明的哈希映射中获取类,这不会抑制警告。 - Jay

4

您需要将对Class的引用泛型化。例如:

Class<Integer> type = Integer.class
Integer i = verifyType("100",type);

会很好地工作。

同样可以:

Map<String, Class<?>> format = new HashMap<String, Class<?>>();

然而,这永远不会奏效:
Integer i = verifyType("100",format.get("Vendor Number"));

由于格式未定义为:

Map<String, Class<Integer>>

如果是这样,铸造工作可以完成,但设计就毫无意义。

最接近的可能是:

Integer i = verifyType("100",(Class<Integer>) format.get("Vendor Number"));

然而,如果你这么做,你会获得一个编译器警告,因为这是一个本质上不安全的转换。编译器接受你的承诺,即format.get语句将返回一个整数。如果你确信这一点,那就是不安全转换的用途。如果你想摆脱编译器警告,可以这样做:

    Class<?> type = format.get("Vendor Number");
    Integer i = null;
    if (type == Integer.class) {
        i = verifyType("100", Integer.class);
    } else {
        //What do you want to do?
    }

不需要输入Integer i = verifyType("100",(Class<Integer>) format.get("Vendor Number"));,你可以写成:Integer i = (Integer) verifyType("100", format.get("Vendor Number"));。这样省去了一些打字的工作。 - Tadeusz Kopec for Ukraine

2
由于您的HashMap返回了一个
Class 

对象,但是 verify 函数期望的是一个

Class<T> 

对象。


好的...在我在这里提问之前,我确实尝试过这个:Map<String, Class<T>> format = new HashMap<String, Class<T>>();但编译器告诉我“无法将T解析为类型”。 - Jay
1
@Jay,Class<T> 的问题在于你必须有一个定义好的参数。你需要的是 Class<?>。 - Yishai
@Yishai - 谢谢,解决了问题。但是编译器抱怨verifyType方法的返回值:\npublic static <T> T verifyType(String value, Class<T> type) throws LKNException { if (value != null) { if (type == Integer.class) { try { return type.cast(new Integer(value)); } catch (Exception err) { }} - Jay
天啊!我怎么在注释中添加新行!! - Jay

1
你会遇到这个错误的原因是,在第一种情况下,编译器看到你传递了一个Class对象,并能够在编译时将T绑定到Integer,但在第二种情况下,编译器只看到你正在传递一个Class对象。
最终,你将无法完成。
Integer i = verifyType("100",format.get("Vendor Number"));

以类型安全的方式实现,因为编译器无法知道您将获得一个整数(如果在该调用之前有人执行 format.put("Vendor Number", X.class) 怎么办?)

1

对我来说很奇怪,你的在线编译器

Integer i = verifyType("100",format.get("Vendor Number"));

抱怨未经检查的调用,但不显示错误,如“类型不匹配:无法从Object转换为Integer”。

这一行对我来说毫无意义。如果verifyType作为第二个参数接收Date.class,您期望它返回什么?还是一个整数(那么verifyType的返回类型无效)?还是日期?如果您期望一个日期,编译器如何知道

format.get("Vendor Number");

返回 Integer.class,但是

format.get("Vendor Dispatch");

返回 Date.class

调用

Integer i = verifyType("100",format.get("Vendor Dispatch"));

必须失败,因为日期不是整数。所以,在我的观点(以及编译器的观点)中,编译

Integer i = verifyType("100",format.get("Vendor Number"));

必须失败。


tkopec,我不同意。我意识到我所尝试的与将不同类型的对象添加到列表中(如在java1.4中)没有什么区别-我认为编译器会在编译时发出警告(它现在正在做),并在运行时抛出类转换异常。它在编译时失败是没有意义的。 - Jay
什么?如果你从一个普通的List中取出一个元素,你必须进行强制类型转换。你不能写Integer i = list.get(0);它必须是Integer i = (Integer) list.get(0);你的例子也是同样的情况 - 为了编译,它必须是Integer i = (Integer) verifyType("100",format.get("Vendor Number"));为了消除警告,将format变量的类型更改为Map<String, Class<?>> format = new HashMap<String, Class<?>>(); - Tadeusz Kopec for Ukraine
@tkopec 理解了。如果我将它声明为Class <?>,编译器将会抑制警告。 那么,从我的verifyType(String val,Class <T> type>方法中返回什么?Object?? - Jay

0

Class 类本身就是一个通用类。如果你尝试

Class<Integer> type = Integer.class;
Integer i = verifyType("100", type);

它应该工作得更好。将输入参数泛型化可以让编译器相信你知道自己在做什么,足以让你的代码在没有警告的情况下编译。

你也可以通过抑制警告来说服编译器使用

@SuppressWarning("unchecked")

在特定的代码行或方法之前。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接