2

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?

midnite
  • 5,157
  • 7
  • 38
  • 52
  • If you choose to ignore or suppress compiler warnings, then yes, you can wreak all kinds of havoc. If you pay attention to the compiler warnings, the situation you're describing can never happen (except through reflection). – VGR Sep 03 '13 at 00:40
  • @VGR yup there is a `unchecked cast` warning. But i do mean someone could intentionally break (crack) this enforcement. – midnite Sep 03 '13 at 00:42

1 Answers1

5

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.

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • Thanks for the nice answer. Is it a better way if we actually accept a `T typeObject` instead of `Class`? `OnlyIntegerTypeAllowed valid = new OnlyIntegerTypeAllowed((Number) new Integer(1));` So we are sure that the Type Parameter defined is a super-class of the "correct" type, because only casting to super-class is compile-error free. However, it leads to a limitation that some types are not able to instantiation (e.g. recursive definition) cannot use this structure. – midnite Sep 03 '13 at 01:14
  • @midnite I'm inclined to say no, that the caller should just be expected to use generics properly, as part of the contract. But really it depends on what you're trying to do. Clearly a type called `OnlyIntegerTypeAllowed` wouldn't be generic at all and just deal with `Integer`s directly, but I realize it's a contrived example. – Paul Bellora Sep 03 '13 at 01:30
  • @midnite Looking at [your original question](http://stackoverflow.com/questions/18581788), it seems you're trying to use the "self type", but this isn't really supported by the language (see my answer [here](http://stackoverflow.com/a/7355094/697449)). I recommend updating/clarifying your original question. – Paul Bellora Sep 03 '13 at 01:31