2

考虑类:

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()));

上面两行都没有编译错误,也没有抛出异常!

OMG - 有更好的方法来执行类型参数吗?

4

1 回答 1

5

欢迎输入擦除。在运行时,Class<T>已被擦除Class- 到 JVM,源代码中的内容 a Class<Integer>Class<String>等看起来都一样。这就是未经检查的演员表的含义——开发人员这样做是自担风险,因为如果它是错误的,它不会很快失败。ClassCastException相反,一些稍后ClassCastException可能会发生,在类型擦除过程中由编译器插入的强制转换中。这种状态,其中一般类型的引用指向它们不应该被允许的对象,被称为堆污染

OMG - 有更好的方法来执行类型参数吗?

不,这是 Java 在泛型类型安全方面所能提供的最好的——它确实是可选的。惰性或滥用代码可以自由地执行未经检查的强制转换或使用原始类型(这会将隐式未经检查的强制转换带入它们所接触的任何内容),尽管许多 IDE 提供这些编译器错误而不是警告。

附带说明一下,未经检查的强制转换有时是有效的,例如在实现 Joshua Bloch 的 Effective Java item 27,“favor generic methods”时:

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;
}

在这里,未经检查的强制转换是安全的,因为它的HASH_CODE_COMPARATOR行为是逆变的。它是无状态的并且适用于 any Object,所以我们可以让调用者决定它的泛型类型:

Comparator<String> c = hashCodeComparator();

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

于 2013-09-03T00:32:32.097 回答