Jens
Jens

Reputation: 9416

Restrictions on Java intersection types with interfaces classes and type variables

Today I tried to write a class with a generic method that uses intersection types and got confused by the different error messages depending on the intersected types. Let's assume that we have an interface and a class and define generic methods in a generic interface:

class ClassType {
}

interface InterfaceType {
}

interface I<T> {
    public <X extends InterfaceType & InterfaceType> void foo();

    public <X extends ClassType & ClassType> void foo1();

    public <X extends ClassType & InterfaceType> void foo2();

    public <X extends InterfaceType & ClassType> void foo3();

    public <X extends T & ClassType> void foo4();

    public <X extends ClassType & T> void foo5();

    public <X extends InterfaceType & T> void foo6();

    public <X extends T & InterfaceType> void foo7();
}

Compiling this produces errors for all method except for public <X extends ClassType & InterfaceType> void foo2();.

Main.java:8: error: repeated interface
    public <X extends InterfaceType & InterfaceType> void foo();
                                      ^
Main.java:10: error: interface expected here
    public <X extends ClassType & ClassType> void foo1();
                                  ^
Main.java:14: error: interface expected here
    public <X extends InterfaceType & ClassType> void foo3();
                                      ^
Main.java:16: error: a type variable may not be followed by other bounds
    public <X extends T & ClassType> void foo4();
                          ^
Main.java:18: error: unexpected type
    public <X extends ClassType & T> void foo5();
                                  ^
  required: class
  found:    type parameter T
  where T is a type-variable:
    T extends Object declared in interface I
Main.java:20: error: unexpected type
    public <X extends InterfaceType & T> void foo6();
                                      ^
  required: class
  found:    type parameter T
  where T is a type-variable:
    T extends Object declared in interface I
Main.java:22: error: a type variable may not be followed by other bounds
    public <X extends T & InterfaceType> void foo7();
                          ^
7 errors

Since intersection should be symmetric, I am surprised that foo2 is accepted but foo3 is rejected. Why is this one case accepted?

I am also wondering in general why there is a distinction between interfaces, classes and type parameters when it comes to intersection types. I can see reasons to not allow more than one class in an intersection type, but the current state accepting ClassType & InterfaceType but not InterfaceType & ClassType seems strangely arbitrary. An intersection A & A is also forbidden, but this is semantically identical to A so I don't see a reason for this.

I am also curious why type variables are not allowed in the intersection. Worst case would be an intersection of two or more classes, but would just be an uninhabited type, so the bottom type.

Upvotes: 5

Views: 2571

Answers (2)

user5459652
user5459652

Reputation:

This is explained in the section 4.4 Type Variables of The Java Language specification

A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.

A type variable is introduced by the declaration of a type parameter of a generic class, interface, method, or constructor (§8.1.2, §9.1.2, §8.4.4, §8.8.4).

TypeParameter:
    {TypeParameterModifier} TypeIdentifier [TypeBound]

TypeParameterModifier:
   Annotation

TypeBound:
    extends TypeVariable
    extends ClassOrInterfaceType {AdditionalBound}

AdditionalBound:
    & InterfaceType

The scope of a type variable declared as type parameter is specified in §6.3.

Every type variable declared as a type parameter has a bound. If no bound is declared for a type variable, Object is assumed. If a bound is declared, it consists of either:

  • a single type variable T, or
  • a class or interface type T possibly followed by interface types I1 & ... & In.

It is a compile-time error if any of the types I1 ... In is a class type or type variable.

The erasures (§4.6) of all constituent types of a bound must be pairwise different, or a compile-time error occurs.

A type variable must not at the same time be a subtype of two interface types which are different parameterizations of the same generic interface, or a compile-time error occurs.

The order of types in a bound is only significant in that the erasure of a type variable is determined by the first type in its bound, and that a class type or type variable may only appear in the first position.

The members of a type variable X with bound T & I1 & ... & In are the members of the intersection type (§4.9) T & I1 & ... & In appearing at the point where the type variable is declared.

Upvotes: 1

isaace
isaace

Reputation: 3429

You can only have 1 class but have multiple interfaces. If you have a class it must be the first one specified. If you follow this rule, you shouldn't get any compiling errors.

See https://docs.oracle.com/javase/tutorial/java/generics/bounded.html

Upvotes: 2

Related Questions