Search code examples
javatypestype-theory

Java: Why must user satisfy extends constraint?


Consider this code:

public class Enclosing {
    class A<X extends Y, Y> {}

    <U, V> void foo(A<U,V> a) {}
}

This gives me an error:

Type parameter U is not within its bound; should extend V.

However, it should be impossible to call this function unless you can construct an instance of A, so why does the declaration of foo need to ensure that U is within its bound? I understand that you can violate this by passing in null, but my intuition would be that soundness still only requires that the "U extends V" constraint be provable when an A is constructed, not when it's used.

So, why is Java requiring that "U extends V" be provable in this declaration? Why is the type system designed this way?


Solution

  • Although allowing <U, V> void foo(A<U,V> a) isn't harmful because you could never construct an instance of a if U didn't extend V (although, as you say, you could validly pass null), it's not helpful either for the compiler to not flag a logical impossibility as soon it sees it.

    In the case that you show, the type of the parameter a is a logical impossibility. The type is A with type parameters U and V where U is not constrained to be a subtype of V. We cannot speak of this type A, since they only type A that exists is the type A where the first type argument is constrained to be a subtype of the second type argument.

    If the declaration was allowed as you suggest, then this declaration should also be allowed:

    void foo(A<String,Integer> a);
    

    Because in your reasoning, "soundness still only requires that the "String extends Integer" constraint be provable when an A is constructed, not when it's used."

    Which could be a way to go about this, but I think most people would prefer the Java compiler to reject impossibilities when it sees them.