Generic Type Argument checked by Class Parameter can be hacked, any better ways?

给你一囗甜甜゛ 提交于 2019-12-09 03:21:50

问题


Consider the class:

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

It is designed to accept only Type Arguments of Integer. We have added a if-throw check in its constructor. This is a very common way to check the Type Arguments.

However, this checking can be bypassed (hacked, fooled) by:

OnlyIntegerTypeAllowed<Integer> normalWay =
        new OnlyIntegerTypeAllowed<Integer>(Integer.class);
OnlyIntegerTypeAllowed<String> hacking =
        new OnlyIntegerTypeAllowed<String>((Class<String>) Class.forName(Integer.class.getName()));

Both the above two lines have no compile-errors and no exceptions thrown!

O.M.G. - any better way to enforce the Type Argument?


回答1:


Welcome to type erasure. At runtime, Class<T> has been erased to Class - to the JVM, what was in the source code a Class<Integer>, Class<String>, etc. all look the same. This is the meaning of an unchecked cast - the developer does it at his or her own peril, because it will not fail fast with a ClassCastException if it's wrong. Instead, some later ClassCastException may occur instead, at a cast that was inserted by the compiler during the type erasure process. This state, in which generically-typed references are pointing to objects they shouldn't have been allowed to, is known as heap pollution.

O.M.G. - any better way to enforce the Type Argument?

No, this is the best Java can offer in terms of generic type safety - it really is opt-in. Lazy or abusive code is free to do unchecked casts or use raw types (which bring implicit unchecked casts to anything they touch), although many IDEs offer to make these compiler errors instead of warnings.

As a side note, unchecked casts are occasionally valid, for example when implementing Joshua Bloch's 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;
}

Here, the unchecked cast is safe because HASH_CODE_COMPARATOR behaves contravariantly. It's stateless and works for any Object, so we can let the caller decide its generic type:

Comparator<String> c = hashCodeComparator();

In this case we can use @SuppressWarnings("unchecked") to remove the unchecked warning, essentially telling the compiler to trust us. It's also a good idea to add an explanatory comment.



来源:https://stackoverflow.com/questions/18582360/generic-type-argument-checked-by-class-parameter-can-be-hacked-any-better-ways

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!