简短的回答:不,您不能摆脱警告.他们在那里是有原因的.
更长的答案:您可能已经知道,Java中的泛型只是语法糖加上编译时检查.几乎没有任何东西可以生存到运行时(称为“擦除”的过程).这意味着您方法中的(T)强制转换实际上是无操作的.它将转换为最具体的类型,在这种情况下为Object.所以这:
(T) (Boolean) prefs.whatever()
真的变成这样:
(Object) (Boolean) prefs.whatever()
当然与以下内容相同:
(Boolean) prefs.whatever()
这可能会使您陷入危险境地,而警告正试图告诉您.基本上,您将丢失类型安全性,并且最终可能使您远离错误的实际位置(因此很难进行跟踪).想象以下情况:
// wherever you see "T" here, think "Object" due to erasure
public void prefsToMap(String key, T defaultValue, Map map) {
T val = retrieve(this.context, key, defaultValue);
map.put(key, val);
}
Map map = new HashMap<>();
prefsToMap("foo", 123, map);
// ... later
Integer val = map.get("foo");
到目前为止,一切都很好,并且在您的情况下也可以使用,因为如果“ foo”位于首选项中,则您将调用getInt来获取它.但是,请想象一下,如果您的检索函数中有一个错误,例如if(Integer的defaultValue instance)意外返回了getDouble()而不是getInt()(带有强制转换以及所有这些).编译器不会捕获它,因为您强制转换为T实际上只是强制转换为Object!在Integer val = map.get(“ foo”);之前,您不会发现它,它变为:
Integer val = (Integer) map.get("foo"); // cast automatically inserted by the compiler
此强制转换可能与错误真正发生的地方(getObject调用)相距很远,这使得很难进行跟踪. Javac试图保护您免受此侵害.
这是所有内容放在一起的示例.在此示例中,我将使用Number代替prefs对象,只是为了使事情保持简单.您可以复制粘贴此示例,然后按原样尝试.
import java.util.*;
public class Test {
@SuppressWarnings("unchecked")
public static T getNumber(Number num, T defaultVal) {
if (num == null)
return defaultVal;
if (defaultVal instanceof Integer)
return (T) (Integer) num.intValue();
if (defaultVal instanceof String)
return (T) num.toString();
if (defaultVal instanceof Long)
return (T) (Double) num.doubleValue(); // oops!
throw new AssertionError(defaultVal.getClass());
}
public static void getInt() {
int val = getNumber(null, 1);
}
public static void getLong() {
long val = getNumber(123, 456L); // This would cause a ClassCastException
}
public static void prefsToMap(Number num, String key, T defaultValue, Map map) {
T val = getNumber(num, defaultValue);
map.put(key, val);
}
public static void main(String[] args) {
Map map = new HashMap();
Long oneTwoThree = 123L;
Long fourFixSix = 456L;
prefsToMap(oneTwoThree, "foo", fourFixSix, map);
System.out.println(map);
Long fromMap = map.get("foo"); // Boom! ClassCastException
System.out.println(fromMap);
}
}
注意事项:
>最大的优点:即使应该使用泛型来保护我,我还是得到了ClassCastException.不仅如此,我在一段代码中得到了错误,根本没有错误(主要).该错误发生在prefsToMap中,但是main支付了费用.如果映射是实例变量,则可能很难跟踪该错误的引入位置.
>除了使用数字而不是首选项外,我的getNumber与您的检索函数几乎相同
>我故意创建了一个错误:如果defaultVal是Long,我得到的是double(而不是long),并且强制转换为T.但是类型系统无法捕获此错误,这正是未经检查的强制转换试图警告我的内容(警告我它不能捕获任何错误,不一定有错误).
>如果defaultValue是int或String,则一切都会好起来.但是,如果它是Long类型,并且num为null,那么当呼叫站点希望使用Long类型时,我将返回Double这样的值.
>因为我的prefsToMap类仅强制转换为T(如上所述,它是无操作强制转换),所以不会导致任何强制转换异常.在倒数第二行Long fromMap = map.get(“ foo”)之前,我不会例外.
使用javap -c,我们可以看到其中一些在字节码中的样子.首先,让我们看一下getNumber.请注意,对T的强制转换不会显示为任何内容:
public static java.lang.Object getNumber(java.lang.Number, java.lang.Object);
Code:
0: aload_0
1: ifnonnull 6
4: aload_1
5: areturn
6: aload_1
7: instanceof #2; //class java/lang/Integer
10: ifeq 21
13: aload_0
14: invokevirtual #3; //Method java/lang/Number.intValue:()I
17: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: areturn
21: aload_1
22: instanceof #5; //class java/lang/String
25: ifeq 33
28: aload_0
29: invokevirtual #6; //Method java/lang/Object.toString:()Ljava/lang/String;
32: areturn
33: aload_1
34: instanceof #7; //class java/lang/Long
37: ifeq 48
40: aload_0
41: invokevirtual #8; //Method java/lang/Number.doubleValue:()D
44: invokestatic #9; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
47: areturn
48: new #10; //class java/lang/AssertionError
51: dup
52: aload_1
53: invokevirtual #11; //Method java/lang/Object.getClass:()Ljava/lang/Class;
56: invokespecial #12; //Method java/lang/AssertionError."":(Ljava/lang/Object;)V
59: athrow
接下来,看看getLong.注意,它将getNumber的结果强制转换为Long:
public static void getLong();
Code:
0: bipush 123
2: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: ldc2_w #15; //long 456l
8: invokestatic #17; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
11: invokestatic #13; //Method getNumber:(Ljava/lang/Number;Ljava/lang/Object;)Ljava/lang/Object;
14: checkcast #7; //class java/lang/Long
17: invokevirtual #18; //Method java/lang/Long.longValue:()J
20: lstore_0
21: return
最后,这是prefsToMap.请注意,由于它仅处理通用T类型(也称为对象),因此根本不进行任何转换.
public static void prefsToMap(java.lang.Number, java.lang.String, java.lang.Object, java.util.Map);
Code:
0: aload_0
1: aload_2
2: invokestatic #13; //Method getNumber:(Ljava/lang/Number;Ljava/lang/Object;)Ljava/lang/Object;
5: astore 4
7: aload_3
8: aload_1
9: aload 4
11: invokeinterface #19, 3; //InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
16: pop
17: return