preface

Java Generics is a new feature introduced in JDK 5. Generics provide compile-time type-safety checks that allow developers to detect illegal types at compile time.

The nature of generics is parameterized typing, which means that the data type being operated on is specified as a parameter.

Benefits of generics

In the absence of generics, arguments can be “arbitrary” by referring to the type Object. The disadvantage of “arbitrary” is that explicit casts are required, which require the developer to be able to predict the actual parameter types. In the case of a cast error, the compiler may not give an error until runtime, which is itself a security risk.

The advantage of generics, then, is that type safety can be checked 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(a) { return t; }
  
    public static void main(String[] args) {
        // do nothing
    }

  /** ** does not specify the type */
  public void noSpecifyType(a){
    GlmapperGeneric glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");
    // A cast is required
    String test = (String) glmapperGeneric.get();
    System.out.println(test);
  }

  /** * Specifies the type */
  public void specifyType(a){
    GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");
    // No casting is requiredString test = glmapperGeneric.get(); System.out.println(test); }}Copy the code

SpecifyType methods in this code omit casting to check type safety at compile time and can be used on classes, methods, and interfaces.

Wildcards in generics

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

The usual T, E, K, V?

Essentially, these are wildcards, no difference, just code conventions. For example, in the above code, T can be replaced with any letter between A and Z without affecting the normal operation of the program. However, if T is replaced with other letters, the readability may be weaker. In general, T, E, K, V, right? Here’s the deal:

  • ? Represents an indeterminate Java type
  • T (type) indicates a specific Java type
  • K and V are key values in Java key values
  • E stands for Element

?Unbounded wildcard

Let’s start with a small example. The original text is here.

I have a parent Animal and several subclasses like dog, cat, etc. Now I need a list of animals. My first thought is something like this:

List<Animal> listAnimals
Copy the code

But the boss did have this idea:

List<? extends Animal> listAnimals
Copy the code

Why use wildcards instead of simple generics? Wildcards don’t really mean much when you declare local variables, but they are very important when you declare an argument to 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 );
	/ / an error
    countLegs1(dogs);
}
Copy the code

When countLegs1 is called, it flutters red with the following error message:

So you can use unrestricted wildcards (a question mark in Angle brackets, <? >), indicating that any type can be held. As in countLegs, the last class is qualified, but it doesn’t care what type it is, so all subclasses of Animal passed in can be supported without an error. CountLegs1 doesn’t.

The upper bound wildcard <? extends E>

Last: Declared with the extends keyword to indicate that the parameterized type may be the specified type or a subclass of that type.

Using extends in a type parameter means that the parameter in the generic must be E or a subclass of E. This has two benefits:

  • If the type passed is not E or a subclass of E, the compilation fails
  • The E method can be used in generics, otherwise you have to cast 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 the code

If there are more than one upper limit of type parameters in the type parameter list, separate them with commas

The lower wildcard <? super E>

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

Using super in type parameters indicates that the parameters in the generic must be E or a parent 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 the code

The DST type is “greater than or equal to” SRC, where “greater than or equal to” means that DST represents a greater range than SRC, so a container that can hold DST can also hold SRC.

? And T

? And T are indeterminate types, the difference is that we can operate on T, but right? No, like this:

/ / can
T t = operate();

/ / can't? car = operate();Copy the code

To summarize briefly:

T is a definite type, usually used in the definition of generic classes and generic methods,? Is an indeterminate type that is usually used for calling code and parameters of generic methods and cannot be used to define classes and generic methods.

Difference 1: T is used to ensure consistency of generic parameters

// Use T to ensure consistency of generic parameters
public <T extends Number> void
test(List<T> dest, List<T> src)

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

As in the following code, the convention T is a subclass of Number, but the declaration is a String, so it will float red error.

There is no guarantee that two lists will 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 the code

The above code does not generate an error from the compiler, but when operating inside testNon methods (such as assignment), the dest and SRC types still need to be cast.

Difference 2: Type parameters can be multi-qualified but wildcards cannot

Use the ampersand symbol to set Multi Bounds, specifying that the generic type T must be a common subtype of MultiLimitInterfaceA and MultiLimitInterfaceB, at which point the variable T has all qualified methods and properties. In the case of wildcards, because it is not a deterministic type, it cannot be multiqualified.

Difference 3: Wildcards can be superclass-qualified while type parameters cannot

The type parameter T has only one type qualification:

T extends A
Copy the code

But wildcards? Two kinds of qualification can be made:

? extends A
? super A
Copy the code

Class<T>Class<? >The difference between

That was introduced, right? And T, so for Class

and

< p style = “max-width: 100%; clear: both; min-height: 1em; Class < T > Class

>

The most common use is in reflection scenarios, which are illustrated here with an emission code.

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

For the above code at runtime, if the reflective type not MultiLimit class, so must be submitted to the Java lang. ClassCastException error.

In this case, you can use the following code instead to check for type problems directly at compile time:

Class

When instantiated, T is replaced with a concrete Class. Class
< span style = “box-sizing: border-box! Important; Can represent any type, so it is mainly used for declarative restrictions. For example, we can declare this:

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

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

Public Class

clazzT; In this case, you have to make the current class also specify T,

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

summary

This article has pieced together a few points in JAVA generics for your reference. If there are any improper places in the article, please correct them.

reference

  • JAVA generic wildcard T, E, K, V difference