由类参数检查的泛型类型参数可以被黑客攻击,有更好的方法吗?

mid*_*ite 2 java generics class generic-programming type-parameter

考虑班级:

class OnlyIntegerTypeAllowed<T> {
    OnlyIntegerTypeAllowed(Class<T> clazz) {
        System.out.println(clazz);
        if (clazz != Integer.class)
            throw new RuntimeException();
    }
}
Run Code Online (Sandbox Code Playgroud)

它被设计为只接受Type Arguments Integer.我们if-throw在其构造函数中添加了一个检查.这是检查类型参数的一种非常常见的方法.

但是,可以通过以下方式绕过(黑客入侵,欺骗)此检查:

OnlyIntegerTypeAllowed<Integer> normalWay =
        new OnlyIntegerTypeAllowed<Integer>(Integer.class);
OnlyIntegerTypeAllowed<String> hacking =
        new OnlyIntegerTypeAllowed<String>((Class<String>) Class.forName(Integer.class.getName()));
Run Code Online (Sandbox Code Playgroud)

无论上述两行有没有编译错误和没有抛出异常!

OMG - 强制执行Type Argument的更好方法吗?

Pau*_*ora 5

欢迎输入类型擦除.在运行时,Class<T>已经删除Class-到JVM,究竟是什么在源代码中的Class<Integer>,Class<String>等看起来都一样.这是一个未经检查的演员的意思- 开发人员在自己的危险中做到这一点,因为如果它错了就不会快速失败ClassCastException.相反,稍后ClassCastException可能会在类型擦除过程中由编译器插入的转换中发生.这种状态,其中一般类型的引用指向它们不应该被允许的对象,被称为堆污染.

OMG - 强制执行Type Argument的更好方法吗?

不,这是Java在通用类型安全方面可以提供的最好的 - 它确实是选择性的.懒惰或滥用代码可以自由地进行未经检查的强制转换或使用原始类型(将隐式未经检查的强制类型转换为它们触摸的任何内容),尽管许多IDE提供了这些编译器错误而不是警告.

作为旁注,未经检查的强制转换偶尔也是有效的,例如在实现Joshua Bloch的Effective Java第27项"赞成通用方法"时:

private static final Comparator<Object> HASH_CODE_COMPARATOR =
        new Comparator<Object>() {
            @Override
            public int compare(final Object o1, final Object o2) {
                return Integer.compare(o1.hashCode(), o2.hashCode());
            }
        };

public static <T> Comparator<T> hashCodeComparator() {
    @SuppressWarnings("unchecked") // this is safe for any T
    final Comparator<T> withNarrowedType =
            (Comparator<T>)(Comparator<?>)HASH_CODE_COMPARATOR;
    return withNarrowedType;
}
Run Code Online (Sandbox Code Playgroud)

在这里,未经检查的演员是安全的,因为HASH_CODE_COMPARATOR行为相反.它是无状态的,适用于任何人Object,所以我们可以让调用者决定它的泛型类型:

Comparator<String> c = hashCodeComparator();
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们可以使用@SuppressWarnings("unchecked")删除未经检查的警告,实质上告诉编译器信任我们.添加解释性注释也是一个好主意.