通过类参数检查的java泛型类型参数可以被黑客攻击,有更好的方法吗?
考虑类:
class OnlyIntegerTypeAllowed<T> {
OnlyIntegerTypeAllowed(Class<T> clazz) {
System.out.println(clazz);
if (clazz != Integer.class)
throw new RuntimeException();
}
}
它被设计为只接受Integer
的类型参数。我们在其构造函数中添加了一个if-throw
检查。这是检查类型参数的一种非常常见的方法
但是,可以通过以下方式绕过(黑客、愚弄)此检查:
OnlyIntegerTypeAllowed<Integer> normalWay =
new OnlyIntegerTypeAllowed<Integer>(Integer.class);
OnlyIntegerTypeAllowed<String> hacking =
new OnlyIntegerTypeAllowed<String>((Class<String>) Class.forName(Integer.class.getName()));
以上两行都有no编译错误和no异常抛出
O.M.G.-有没有更好的方法来强制执行类型参数
# 1 楼答案
欢迎来到type erasure。在运行时,
Class<T>
被擦除到Class
-到JVM,源代码中的内容aClass<Integer>
,Class<String>
等等。所有这些看起来都是一样的。这就是unchecked cast的含义——开发人员这样做的风险由他或她自己承担,因为如果ClassCastException
错误,它不会快速失败。相反,在类型擦除过程中编译器插入的强制转换中,可能会出现一些稍后的ClassCastException
。这种状态称为heap pollution,在这种状态下,泛型类型的引用指向不应该允许的对象不,就泛型类型安全而言,这是Java所能提供的最好的——它实际上是选择性加入。懒惰或滥用的代码可以自由地执行未经检查的强制转换或使用raw types(这会将隐式未经检查的强制转换带到它们接触的任何对象),尽管许多IDE提供了使这些编译器出错而不是警告的功能
作为旁注,未经检查的强制类型转换偶尔是有效的,例如,在实现Joshua Bloch的有效Java项目27“偏爱泛型方法”时:
在这里,未经检查的强制转换是安全的,因为
HASH_CODE_COMPARATOR
的行为是contravariantly。它是无状态的,适用于任何Object
,因此我们可以让调用者决定其泛型类型:在这种情况下,我们可以使用
@SuppressWarnings("unchecked")
删除未检查的警告,本质上是告诉编译器信任我们。添加解释性注释也是一个好主意