Java generics are a new feature introduced in JDK 5. Generics provide a compile-time type safety detection mechanism that allows developers to detect illegal types at compile time.


Generics are essentially parameterized types, meaning that the type of data being manipulated is specified as a parameter.

 Benefits of generalization


In the absence of generalization, parameter “arbitrariness” is achieved through references to the type Object. The disadvantage of “arbitrariness” is that it requires explicit forced type conversion, which requires the developer to know the actual parameter type. The disadvantage of “arbitrariness” is the explicit forced type conversion, which requires the developer to be able to predict the actual parameter type. In the case of a forced type conversion error, the compiler may not indicate the error, and the exception occurs only at runtime, which is a security risk in itself.


The benefit of generalization then is the ability to check for type safety at compile time, and all forced conversions 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
    }


  public void noSpecifyType(){
    GlmapperGeneric glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");

    String test = (String) glmapperGeneric.get();
    System.out.println(test);
  }

  public void specifyType(){
    GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric();
    glmapperGeneric.set("test");
    String test = glmapperGeneric.get();
    System.out.println(test);
  }
}


The specifyType method in the above code omits the mandatory conversion and can be used to check for type safety at compile time for classes, methods, and interfaces.

 Wildcards in generalizations


When defining generic classes, generic methods, and generic interfaces, we often run into a lot of different wildcards, such as T, E, K, V, etc. What do these wildcards mean?

 Commonly used T, E, K, V, ?


These are essentially wildcards, no difference, just a coding convention. For example, we can replace the T in the above code with any letter between A and Z without affecting the normal operation of the program, but if we replace the T with another letter, it may be less readable. Typically, T, E, K, V, ? are agreed upon:


  • ? Indicates an indeterminate java type
  •  T (type) represents a specific java type.

  • K V (key value) represents the Key Value in the java key value, respectively.
  •  E (element) stands for Element

 ? Unbounded wildcards

 Let’s start with a small example, the original article is here.


I have a parent class Animal and several subclasses such as dog, cat, etc. now I need a list of animals and my first idea is something like this:

List<Animal> listAnimals

  But the boss’s idea does:

List<? extends Animal> listAnimals


Why use wildcards instead of simple generalizations? Wildcards are actually 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<>();

    countLegs( dogs );

    countLegs1(dogs);
}


When countLegs1 is called, it floats red, prompting the following error message:


So, for those who are unsure or don’t care about the actual type to be manipulated, you can use an unrestricted wildcard (a question mark in pointed brackets, i.e., <?> ) to indicate that any type can be held. Like in the countLegs method, which qualifies the upper term, but doesn’t care what the specific type is, so it’s supported for all subclasses of Animal that are passed in and doesn’t report an error. countLegs1 does not.


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.


The use of extends in a type parameter indicates that the parameter in this generic must be E or a subclass of E. This has two advantages:


  • If the type passed in is not E or a subclass of E, compilation fails.

  • E’s methods can be used in generalizations that would otherwise have to be converted to E in order to be used.
private <K extends A, E extends B> E test(K arg1, E arg2){
    E result = arg2;
    arg2.compareTo(arg1);
    //.....
    return result;
}


If there is more than one type parameter cap 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 a parent of this type, up to Object.


The use of super in a type parameter means that the parameter in this 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);
}

class Dog extends Animal {

}


The type dst is “greater than or equal to” the type of src, where “greater than or equal to” means that dst represents a larger range than src, so a container that can hold dst can also hold src.


? Difference with T


? and T both represent indeterminate types, the difference being that we can operate on T but not on ? but not on ?, as in :


T t = operate();

? car = operate();

  To summarize briefly:


T is a deterministic type typically used in the definition of generic classes and generic methods, ? is an indeterminate type typically used for generic method call codes and formal parameters, not for defining classes and generic methods.


Difference 1: Ensuring consistency of generic parameters via T


public <T extends Number> void
test(List<T> dest, List<T> src)

public void
test(List<? extends Number> dest, List<? extends Number> src)


In the following code, the convention is that T is a subclass of Number, but the declaration is in String, so the error is red.


Cases where two Lists are not guaranteed to have the same element type

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


The above code doesn’t report errors in the compiler, but when it comes to the inner workings of the testNon method (e.g., assignments), type conversions are still required for dest and src.

 Difference 2: Type parameters can be multi-qualified while wildcards can’t


Using the & symbol to set Multi Bounds specifies that the generic type T must be a common subtype of MultiLimitInterfaceA and MultiLimitInterfaceB, and that the variable t then has all the qualified methods and properties. In the case of a wildcard, because it is not a defined type, it cannot be multi-qualified.

 Difference 3: Wildcards can be qualified with superclasses but not type parameters


The type parameter T has only one type qualification:

T extends A


But the wildcard ? can be qualified in two ways:

? extends A
? super A

  Class<T> Distinction from Class<?>


What is the difference between ? and T, what is the difference between, Class<T> and <Class<?> ?

  Class<T> respond in singing Class<?>


The most common use is in reflection scenarios, which are illustrated here with a piece of emitting code.


MultiLimit multiLimit = (MultiLimit)
Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();


For the above code, at runtime, if the reflected type is not the MultiLimit class, then a java.lang.ClassCastException error will definitely be reported.


In this case, the following code can be used instead, making it possible to check for type problems directly at compile time:


Class<T> When instantiated, T is to be replaced with the concrete class. Class<?> It is a generic generalization, ? can represent any type, so it is mainly used in restricted situations when declaring. For example, we can make declarations like this:


public Class<?> clazz;

public Class<T> clazzT;


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


Then if you want public Class<T> clazzT; to do the same, you have to make the current class specify T , too.

public class Test3<T> {
    public Class<?> clazz;      public Class<T> clazzT;

 

By lzz

Leave a Reply

Your email address will not be published. Required fields are marked *