Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I tried to rewrite the bug into a simpler example to wrap my mind around it:

    public interface Helper<T extends Number> {
    }

    public static <T extends Number> Number nice(Helper<T> helper, T value) { // (1)
        return value; 
    }

    public static <SUB_T> Number naughty(Helper<? super SUB_T> helper, SUB_T value) { // (2)
        return nice(helper, value); // (3)
    }

    public static Number evil(String value) {
        return naughty(null, value); // (4)
    }
From what I understand, the following is happening:

1: Nothing unusual here. Just defining a type parameter with a constraint and referencing another type which happens to have the same constraint.

2: I think this is the actual bug (?) - SUB_T is permitted as the subtype of a constrained type, yet itself is not subject to the constraint. This compiles.

3: The compiler derives T from pattern matching (this is the sole reason why the helper is needed)

T is inferred as the set of hypothetical types that are both subtypes of Number and supertypes of SUB_T.

4: Because SUB_T is unconstrained, we can happily instantiate SUB_T such that the set of hypothetical types is empty (e.g. set it to String).

This does mean there is no way we could create a helper object as no type would satisfy the constraints - but we don't actually need a helper object, so we can simply pass null.

In short, this bug lets the compiler infer a type hierarchy of Number -> T -> SUB_T without ensuring that Number -> SUB_T holds. Usually, that wouldn't get you far as there is no type that could substitute for T - except that you don't need a type because the compiler will accept null even for "impossible" types.

It's interesting to note that the class cast exception occurs in (3), not in (1). From what I know, that is because Java's erasure takes constraints into account - the arguments of nice() become (Helper, Number) after erasure, not (Helper, Object).



yes, (2) is the bug. funny that my IDE catches it, but not the compiler!


The article mentions that some compilers catch it, but that by doing so they don't comply with the language definition.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: