Home > Software engineering >  Java generics lower bound
Java generics lower bound

Time:11-29

I understand upper bound clearly, but don't fully understand lower bound. As for example I have this code:

public class Main<T> {
    private T t;

    public Main(T t) {
        this.t = t;
    }

    private static class Base {}

    public static void main(String[] args) {
        Main<? super Base> main = new Main<>(new StringBuilder());
        System.out.println(main.t.getClass());
    }
}

Why there is no error during compile despite of StringBuilder isn't super class of Base. As I thought it would be illegal to provide type which is irrelevant (I know that it's impossible to assign non child class to t after type inference). Also it works with collections, does it means that collection could possibly store no child, no super class of Base? Please do not link me to PECS questions, I have read them a lot.

CodePudding user response:

Edit: I saw it a little late that @markspace's answer is nearly the same and was posted before this. I am only retaining this here due different styles of explanation, that is all.


This should be due to the diamond operator <> due to which automatic inference happens based on the most possible route, in this case the constructor parameter.

I could not locate the official description of the diamond operator, but various sources describe it to the effect - the Java compiler does a type inference when given the diamond operator feature determines the most suitable constructor declaration that matches the invocation.

If you change your declaration to GenericsSuper<? super Base> main = new GenericsSuper<StringBuilder>(new StringBuilder()) you will get your expected error.

Without this explicit declaration, <> leads to <Object> due to the super restriction, and consequently anything is allowed, because:

  • The code in GenericsSuper has no problem with StringBuilder. It just wants T.
  • Base has a common super with StringBuilder in Object.

CodePudding user response:

I'm going to add this as an actual answer.

I think <T> here is Object, which is a legal super type of both Base and StringBuilder. The way you are doing this / thinking about this is flawed, basically. Check out the answer to this question (ignore the duplicate text for PECS):

Difference between <? super T> and <? extends T> in Java

Notice the examples that the accepted answer gives, especially the one with <Object>:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer

Since you're allowing the diamond operator to "figure out" the type of T, it "figures out" that Object works and uses that. Untested, but check if this also compiles:

Main<? super Base> main = new Main<Object>(new StringBuilder());
  • Related