Generic extensions and super wildcards

extends wildcard

First define a class:

class User<T> {
    private T t1;
    private T t2;

    public User() {
    }

    public User(T t1, T t2) {
        this.t1 = t1;
        this.t2 = t2;
    }

    public T getT1() {
        return t1;
    }

    public void setT1(T t1) {
        this.t1 = t1;
    }

    public T getT2() {
        return t2;
    }

    public void setT2(T t2) {
        this.t2 = t2;
    }
}

There is an add() method, and the parameter type is User:

public static int add(User<Number> user) {
    Number t1 = user.getT1();
    Number t2 = user.getT2();
    return t1.intValue() + t2.intValue();
}
  • Calling add() method in main function

    public static void main(String[] args) {
        User<Integer> user = new User<>(123, 356);
        System.out.println(add(user));
    }
    

    The reason is: because user < integer > is not a subclass of user < number >, it will report user < Java Lang. integer > cannot be converted to javase generics. upcasting. User<java. Lang. number > error.

  • To receive user < integer >, you can use user <? extends Number>.

    public static int add(User<? extends Number> user) {
        Number t1 = user.getT1();
        Number t2 = user.getT2();
        return t1.intValue() + t2.intValue();
    }
    

    After this transformation, you can not only receive Integer, but also pass in subclasses such as Double and BigDecimal. This is called the extends wildcard.

What should I pay attention to when using the extends wildcard?

The User class can be simplified into three elements: T t, T get(), and set(T t).

After using the extends wildcard, the above User class can be simply understood as:

class User<? extends Number> {
    private <? extends Number> t;

    public <? extends Number> getT1() {
        return t1;
    }

    public void setT1(<? extends Number> t1) {
        this.t1 = t1;
    }
}

Now try calling the get() and set() methods respectively.

public static int add(User<? extends Number> user) {
    Number t1 = user.getT1();
    Number t2 = user.getT2();
    user.setT1(new Integer(12321));
    user.setT2(new Integer(431243));
    return t1.intValue() + t2.intValue();
}

Result: there is no problem with the get() method, and the set() method cannot be compiled.

reason:

Java generic wipe mechanism: the compiler always converts to Object, and when transformation is needed, the compiler will automatically implement safe forced transformation for us according to the type of T.

  • For the get() method

    In this case, because of the existence of Java generic wipe mechanism, the compiler will <? Extensions number > is processed as an Object. When the result is returned, it will automatically transform safely.

    However, because Java is a static language, the compiler will automatically convert the result to Integer, Double, BigDecimal and other sub types of Number after running. It is unknown before running. Therefore, in this case, if you want to safely receive the returned result, you can only use Number to receive it.

  • For the set() method

    Similarly, because of the generic wipe mechanism, the compiler cannot recognize user <? Which type is extensions number > user, but obviously, set() methods of subtypes other than user < Integer > user cannot pass in Integer type parameters.

    Therefore, in the compilation stage, the set() method will report Java: incompatible type: Java Lang. integer cannot be converted to capture#1, total? extends java.lang.Number error.

super wildcard

Extensions wildcard user <? Extensions Number > can receive subtypes of Number such as Integer, Double and BigDecimal.

The super wildcard is the opposite of the extends wildcard. User<? Super integer > can receive parent types such as Number and Object.

Similarly, the User class can be simplified into three elements: T t, T get(), and set (T).

After using the extends wildcard, the above User class can be simply understood as:

class User<? super Integer> {
    private <? super Integer> t;

    public <? super Integer> getT1() {
        return t1;
    }

    public void setT1(<? super Integer> t1) {
        this.t1 = t1;
    }
}

Also call get() and set():

public static int test(User<? super Integer> user) {
    Integer t1 = user.getT1();
    Integer t2 = user.getT2();
    user.setT1(12321); //Integer auto boxing
    user.setT2(12321);
    return t1 + t2;
}

Result: there is no problem with the set() method, and the get() method cannot be compiled.

reason:

  • For the get() method

    For the same reason as the extends wildcard, due to the wipe mechanism, the compiler will? Super integer > is processed as an Object. When the result is returned, it is automatically transformed safely.

    Due to the static language characteristics of Java, you can only know that the return value may be super types such as Integer type, Number and Object before running.

    Because it is impossible to know which type it is, when Integer is used to receive parameters, Java: incompatible type: capture#1, total? super java.lang.Integer cannot be converted to java.Integer Lang.Integer error.

  • For the set() method

    A subclass object is also a parent object. That is, whether user <? Super Integer > no matter what kind of Integer superclass user is, an Integer object is naturally a superclass (such as a Number object).

    Therefore, an Integer object can always be passed in as a parameter and take effect.

summary

Whether it's <? Extensions number > or <? Super integer > can be understood as a type.

  • In <? In extensions Number >, this type must be a subtype of Number, but we don't know what this type is, so use? Replace.

  • In <? In super Integer >, this type must be a super type of Integer, but we don't know what this type is, so use? Replace.

The two situations described in the previous two sections are actually caused by the wiping mechanism of Java generics. At the same time, the wiping mechanism of Java generics is formulated because of the static language feature that Java needs to be compiled before execution. In order to enable the compiler to help statically correct programs and find errors in advance, Some flexibility must be sacrificed -- the compiler cannot accurately judge the generic type, so as to realize the error reporting of security transformation.

Keywords: Java JavaSE

Added by Rodders on Fri, 28 Jan 2022 15:41:02 +0200