from:https://mp.weixin.qq.com/s/bJGLkcmAIOMQ5ydwlpdSZg
|Foreword
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 is 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. In the case of cast errors, the compiler may not prompt errors, and exceptions occur 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);
}
}
The specififytype method in the above code omits the cast. 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-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
But the boss does think so:
List<? extends Animal> listAnimals
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 );
//Error reporting
countLegs1(dogs);
}
When countLegs1 is called, it will be red, and the error message is as follows:
Therefore, for the type that is uncertain or does not care about the actual operation, You can use infinite wildcards (a question mark in angle brackets, that is, <? >) to indicate that any type can be held. For example, in the countLegs method, the upper bound is limited, but you don't care about the specific type, so all subclasses of the incoming Animal can be supported without error. countLegs1 is 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.
Using extensions in type parameters means that the parameters in this generic type must be e or subclasses 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;
}
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 until 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 {
}
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:
//Yes
T t = operate();
//No
?car = operate();
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 calling code and formal parameters of generic methods, and 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)
//The wildcard character , is 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)
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);
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 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
But wildcards? There are two restrictions:
? extends A
? super A
|Class < T > and class <? > difference
What was introduced earlier? And T, then for class < T > and < class <? > What's the difference? Class < T > and class <? >
The most common is the use in the reflection scene. Here, a piece of emitted code is used to illustrate it.
//Generate multi limit by reflection
//Object, it is obvious here that we need to use cast
MultiLimit multiLimit = (MultiLimit)
Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();
For the above code, if the type of reflection is not MultiLimit class at 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 generic type,? It can represent any type, so it is mainly used for restrictions when declaring. For example, we can state:
//Yes
public Class<?> clazz;
//No, because T # needs to specify the type
public Class<T> clazzT;
So when you don't know what type of Class to declare, you can define a Class <? >.
If you also want to create 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;