The wildcards T, E, K and V in JAVA generics are indistinguishable?

preface

Java generics is a new feature introduced in JDK 5. Generics provides a compile time type safety detection mechanism, which allows developers to detect illegal types at compile time.

The essence of generics is parameterized type, that is, the data type operated on is specified as a parameter.

Benefits of generics

In the absence of generics, the "arbitrary" of parameters can be realized by referring to the type Object. The disadvantage of "arbitrary" is to make explicit forced type conversion, which requires developers to predict the actual parameter types. For the case of cast error, the compiler may not prompt the error, and the exception occurs only when running, which is itself a security risk.

The advantage of generics is that it can check type safety at compile time, and all casts are automatic and implicit.

public class GlmapperGeneric<T> {
		private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
  
    public static void main(String[] args) {
        // do nothing
    }

  /**
    * Do not specify type
    */
  public void noSpecifyType(){
    GlmapperGeneric glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");
    // Cast required
    String test = (String) glmapperGeneric.get();
    System.out.println(test);
  }

  /**
    * Specify type
    */
  public void specifyType(){
    GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");
    // Cast is not required
    String test = glmapperGeneric.get();
    System.out.println(test);
  }
}
Copy code

The specififytype method in the above code omits the mandatory conversion. It can check the type safety during compilation and can be used on classes, methods and interfaces.

Wildcards in generics

When defining generic classes, generic methods and generic interfaces, we often encounter many different wildcards, such as T, E, K, V and so on. What do these wildcards mean?

Commonly used T, E, K, V,?

In essence, these are wildcards, no difference, but a conventional thing in coding. For example, we can replace t in the above code with any letter between a and Z, which will not affect the normal operation of the program, but if we replace T with other letters, it may be less readable. Usually, t, E, K, V,? It is agreed as follows:

  • ? Represents an ambiguous java type
  • T (type) represents a specific java type
  • K V (key value) respectively represents the Key Value in the java Key Value
  • E (element) stands for Element

? Unbounded wildcards

Let's start with a small example,

I have a parent Animal and several subclasses, such as dogs and cats. Now I need a list of animals. My first idea is like this:

List<Animal> listAnimals
 Copy code

But the boss does think so:

List<? extends Animal> listAnimals
 Copy code

Why use wildcards instead of simple generics? Wildcards are meaningless when declaring local variables, but they are very important when you declare a parameter for a method.

static int countLegs (List<? extends Animal > animals ) {
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}

static int countLegs1 (List< Animal > animals ){
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}

public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
 	// No error will be reported
    countLegs( dogs );
	// report errors
    countLegs1(dogs);
}
Copy code

When countLegs1 is called, it will be red, and the error message is as follows:

Therefore, for types that are uncertain or do not care about the actual operation, you can use infinite wildcards (a question mark in angle brackets, i.e. <? >), indicating that you can hold any type. For example, the countLegs method defines the previous session, but does not care about the specific type. Therefore, all subclasses of the incoming Animal can be supported without error. Not countLegs1.

Upper bound wildcard <? extends E>

Previous: declared with the extends keyword, indicating that the parameterized type may be the specified type or a subclass of this type.

Using extensions in type parameters means that the parameters in this generic type must be e or a subclass of E, which has two advantages:

  • If the type passed in is not e or a subclass of E, the compilation will not succeed
  • You can use the method of E in generics, otherwise you have to convert to e to use it
private <K extends A, E extends B> E test(K arg1, E arg2){
    E result = arg2;
    arg2.compareTo(arg1);
    //.....
    return result;
}
Copy code

If there are multiple upper limits of type parameters in the type parameter list, separate them with commas

Lower bound wildcard <? super E>

Lower bound: declared with super, indicating that the parameterized type may be the specified type or the parent type of this type, up to Object

Using super in type parameters means that the parameters in this generic type must be e or the parent class of E.

private <T> void test(List<? super T> dst, List<T> src){
    for (T t : src) {
        dst.add(t);
    }
}

public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = new ArrayList<>();
    new Test3().test(animals,dogs);
}
// Dog is a subclass of Animal
class Dog extends Animal {

}
Copy code

dst type "greater than or equal to" src type. Here "greater than or equal to" means that the range represented by dst is larger than src, so the container that can hold dst can also hold src.

? Difference between and T

? And t both represent uncertain types. The difference is that we can operate on T, but for? No, for example:

// sure
T t = operate();

// may not
? car = operate();
Copy code

To summarize briefly:

T is a definite type, which is usually used for the definition of generic classes and generic methods,? Is an uncertain type, which is usually used for the calling code and formal parameters of generic methods. It cannot be used to define classes and generic methods.

Difference 1: ensure the consistency of generic parameters through T

// Ensure the consistency of generic parameters through T
public <T extends Number> void
test(List<T> dest, List<T> src)

//Wildcards are uncertain, so this method cannot guarantee that two lists have the same element type
public void
test(List<? extends Number> dest, List<? extends Number> src)
Copy code

As in the following code, the agreed T is a subclass of Number, but the declaration uses String, so an error will be reported in red.

There is no guarantee that two lists have the same element type

GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric<>();
List<String> dest = new ArrayList<>();
List<Number> src = new ArrayList<>();
glmapperGeneric.testNon(dest,src);
Copy code

The above code will not report an error in the compiler, but when entering the internal operation of testNon method (such as assignment), type conversion is still required for dest and src.

Difference 2: type parameters can be multi qualified, but wildcards cannot

Use the & symbol to set multi boundaries and specify that the generic type t must be a common subtype of MultiLimitInterfaceA and MultiLimitInterfaceB. At this time, the variable t has all the qualified methods and properties. For wildcards, because it is not a definite type, multiple qualifications cannot be made.

Difference 3: wildcards can use super class qualification, but type parameters cannot

Type parameter T has only one type qualification method:

T extends A
 Copy code

But wildcards? There are two restrictions:

? extends A
? super A
 Copy code

Class < T > and class <? > difference

Previously introduced? And T, so for class < T > and < class <? > What's the difference? Class < T > and class <? >

The most common is the use in the reflection scene. Here we will explain it with a piece of emitted code.

// Generate multiLimit by reflection 
// Object. What is obvious here is that we need to use cast
MultiLimit multiLimit = (MultiLimit)
Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();
Copy code

For the above code, if the type of reflection is not MultiLimit class during the run time, it will report Java Lang.classcastexception error.

In this case, the following code can be used instead, so that the type problem can be directly checked at compile time:

Class < T > when instantiating, t should be replaced with a specific class. Class<?> It's a wildcard generic,? It can represent any type, so it is mainly used for restrictions when declaring. For example, we can state:

// sure
public Class<?> clazz;
// No, because T needs to specify the type
public Class<T> clazzT;
Copy code

So when you don't know what type of Class to declare, you can define a Class <? >.

If you also want to use public class < T > clazzt; In this case, the current class must also specify t,

public class Test3<T> {
    public Class<?> clazz;
    // No error will be reported
    public Class<T> clazzT;
Copy code

Summary

This paper sorts out some points in JAVA generics, which are not very complete, for reference only. If there is anything wrong in the text, please correct it.

Welcome to follow the official account [code Nong Hua Hua] to learn and grow together I will always share Java dry goods, as well as free learning materials, courses and interview books Reply: [Computer] [design mode] surprise

Posted by chowdary on Thu, 05 May 2022 03:01:47 +0300