当前位置:网站首页>2021-03-14 - play with generics

2021-03-14 - play with generics

2022-07-08 01:17:00 Mengqi D Xiaowu

Generic

Write it at the front : Generic programming (Generic programming) It means that the code can be reused by many different types of objects .

Catalog

1 Generic (generic)

2 Why use generics

3 Reusability of generics

4 Generic does not allow subtypes

5 Generic rules

5.1 Type Erasure

5.1.1 Class erasure 、 Method erase

5.1.2 Compiler bridging

5.1.3 Heap pollution

5.2 Translate generic expressions

5.3 Subtype rule

5.3.1 Type boundary

5.3.2 wildcard

6 Limitations of generics

7 Summary

7.1 Two dimensions :

7.2 ArrayList in addAll Method

1 Generic (generic)

Generic stay J2E1.5 Introduced , Introduction in , In short , Generics are defining classes , Interfaces and methods make types ( Classes and interfaces ) Become a parameter . Very similar to the more familiar formal parameters used in method declarations , Type parameters provide you with a way to reuse the same code with different inputs . The difference is that the input of formal parameters is value , The input of type parameter is type .

JDK Is to check the type at compile time , Provides compile time type security . It's a collection The framework increases the security of compile time types , And eliminates the heavy type conversion work .

2 Why use generics

stay JDK5.0 before , If the return value of a method is Object , A collection is filled with Ob ject , Then getting the return value or element can only be forced , If there is a type conversion error , The compiler cannot detect , This greatly increases the probability of program errors , Quality must be guaranteed through continuous testing , Wouldn't it be nice if it could be solved at compile time ?

3 Reusability of generics

obviously , We all know Java The reusability of code is high , But how to realize the so-called reusability ???

This example , You can also skip , After reading the wildcards 、 upper bound 、 Look back at the lower bound .

For reusability of generics , Let's take a direct example :

Take a simple example , Bubble sort the array , Then we can easily write a bubble sort as follows :

    //N A number bubble sort , All in all N-1 A comparison , The sorting times of each trip are (N-i) Compare it to

    public static void bubbleSort(int[] arr){

        // Remember to judge the boundary conditions , Many people don't pay attention to these details , The interviewer doesn't bother to look down when he sees your code , Which project of your code dares to add ?

        if(arr==null||arr.length<2){
            return;
        }

        // Need to carry out arr.length A comparison 

        for(int i = 0 ;i<arr.length-1;i++){

            // The first i A comparison 

            for(int j = 0 ;j<arr.length-i-1;j++){

                // Start a comparison , If arr[j] Than arr[j+1] It's worth a lot , Then swap places 

                if(arr[j]>arr[j+1]){

                    int temp=arr[j];

                    arr[j]=arr[j+1];

                    arr[j+1]=temp;
                }
            }
        }
}

Obviously , This function can deal with the sorting of integer arrays , But for a floating-point array , Then this function cannot handle , The preliminary improvements are as follows :

Maybe you'll say , It can be changed to a floating point , Then you can handle both integer and floating-point types

public  static void bubbleSort(double[] arr) {

              boolean flage = true;

              for (int i = 0; flage && i < arr.length - 1; i++) {

                     flage = false;

                     for (int j = 0; j < arr.length - i - 1; j++) {

                            if (arr[j] > arr[j + 1]) {

                                   double temp = arr[j];

                                   arr[j] = arr[j + 1];

                                   arr[j + 1] = temp;

                                   flage = true;
                            }
                     }
              }
       }

Improve... Again :

  1. What if it is for a character array ????
  2. If it is for a string array ???
  3. What if I want to sort from small to large and from large to small ???
  4. What if I want to sort people according to their weight ????

Obviously, what we need is an array element that can be processed arbitrarily , A way to determine the attributes to compare :

public static <T> void bubbleSort(T[] arr, Comparator cmp) {

              boolean flage = true;

              for (int i = 0; flage && i < arr.length - 1; i++) {

                     flage = false;

                     for (int j = 0; j < arr.length - i - 1; j++) {

                            if (cmp.compare(arr[j], arr[j + 1]) > 0) {

                                   T temp = arr[j];

                                   arr[j] = arr[j + 1];

                                   arr[j + 1] = temp;

                                   flage = true;
                            }
                     }
              }
       }

Now we can deal with the case that the array elements are arbitrary , Just create unused comparators according to your own needs

import java.util.Comparator;
public class FirstMin implements Comparator<Integer> {

       @Override
       public int compare(Integer i1, Integer i2) {
              return i2 - i1;
       }
}



import java.util.Comparator;
public class FirstMax implements Comparator<Integer> {

       @Override
       public int compare(Integer i1, Integer i2) {
              return i1-i2;
       }
}

Last :

Because there is a problem involved in it ,“ Worker ”,“ Student ” All are “ people ”, The two can obviously compare weight .

But the object of the comparator can only be one

So the function must be modified

public static <T> void bubbleSort(T[] arr, Comparator<? super T> cmp) {

              boolean flage = true;

              for (int i = 0; flage && i < arr.length - 1; i++) {

                     flage = false;

                     for (int j = 0; j < arr.length - i - 1; j++) {

                            if (cmp.compare(arr[j], arr[j + 1]) > 0) {

                                   T temp = arr[j];

                                   arr[j] = arr[j + 1];

                                   arr[j + 1] = temp;

                                   flage = true;
                            }
                     }
              }
       }

Come here , Maybe everyone , Don't know much about , The upper and lower bounds of generics are introduced in detail later .

4 Generic does not allow subtypes

 

The answer is yes . Why? ?

Suppose that , Can it be changed into the following situation , stay JDK All classes in are Object Subclasses of , If subtype is allowed , that ls You can store any type of elements in , This is completely contrary to the type constraints of generics , therefore JDK There are strict constraints on the verification of generics .

5 Generic rules

5.1 Type Erasure

5.1.1 Class erasure 、 Method erase

Write two pieces of code at will , A paragraph uses generic , Not for a while . After compilation , Generated .class The file is exactly the same as the original code , It's like the type information passed is erased again .( The specific code will not be repeated )

jdk1.5 Previous code

public class Node{
private Object obj;
public Object get(){
return obj;
}
public void set(Object obj){
this.obj=obj;
}
public static void main(String[] argv){
Student stu=new Student();
Node node=new Node();
node.set(stu);
Student stu2=(Student)node.get();
}
}

Code that uses generics

public class Node<T>{
private T obj;
public T get(){
return obj;
}
public void set(T obj){
this.obj=obj;
}
public static void main(String[] argv){
Student stu=new Student();
Node<Student> node=new Node<>();
node.set(stu);
Student stu2=node.get();
}
}

Generated by two versions .class file

public Node();
Code:
0: aload_0
 You can see that generics is when you use generic code , Pass type information to specific generic code . After compilation , Generate 
 Of .class  The file is exactly the same as the original code , It's like the type information passed is erased again .
 Methods erase cases 
 Compiler bridging 
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
public java.lang.Object get();
Code:
0: aload_0
1: getfield #2 // Field obj:Ljava/lang/Object;
4: areturn
public void set(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field obj:Ljava/lang/Object;
5: return
}



//  Second document 

public class Node<T> {
public Node();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
public T get();
Code:
0: aload_0
1: getfield #2 // Field obj:Ljava/lang/Object;
4: areturn
public void set(T);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field obj:Ljava/lang/Object;
5: return
}

5.1.2 Compiler bridging

The bridging method is JDK 1.5 When generics are introduced , In order to make Java Bytecode and generated by the generic method of 1.5 Compatible with bytecode before version , Methods automatically generated by the compiler .

package com.naixue.vip.p6.bridging;
/**
* @Description
* @Author 
**/
public class Node<T> {
public T data;
public void setData(T data) {
this.data = data;
}
public Node(T data) {
System.out.println("Node.setData");
this.data = data;
}
public static class MyNode extends Node<Integer>{
public MyNode(Integer data) {
super(data);
}
@Override
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// Simulate the bridge method generated by the compiler 
// public void setData(Object data){
// setData((Integer)data);
// }
}
public static void main(String[] args) {
MyNode mn=new MyNode(5);
Node n=mn;
//java.lang.ClassCastException: java.lang.String cannot be cast to
java.lang.Integer
n.setData("Hello");
Integer x=mn.data;
System.out.println(x);
}
}

We can go through Method.isBridge() Method to determine whether a method is a bridging method , In bytecode, the bridging method will be marked ACC_BRIDGE and ACC_SYNTHETIC, among ACC_BRIDGE It is used to explain that this method is a bridge method generated by compilation ,ACC_SYNTHETIC Note that this method is generated by the compiler , And will not appear in the source code . You can see jvm For these two access_flag The explanation of http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6.

There are the following 3 A question

  1. When will the bridge method be generated
  2. Why generate bridging methods
  3. How to get the actual method through the bridging method

a When will the compiler generate bridge methods ? You can see JLS Description in http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.4.5.

That is to say, a subclass inherits ( Or implementation ) A parent class ( Or interface ) When the generic method of , stay Generic types are explicitly specified in subclasses , Then the compiler will automatically generate the bridge method at compile time ( Of course, there are other situations that will generate bridging methods , Here is just one case ).

b Because generics at compile time, the compiler will check whether the type of the object added to the collection matches the generic type , If not, errors will be found during compilation , You don't have to wait until runtime to find errors . Because generics are in 1.5 Introduced , For forward compatibility , So generics will be removed at compile time ( Generic erase ), But we can still reflect API To get information about generics , At compile time, you can ensure the correctness of types through generics , You don't have to wait until runtime to find that the type is incorrect . because java Generic erasure feature , If no bridging method is generated , Then with 1.5 The previous bytecode is incompatible .

c When we make a method call through reflection , What if we get the actual method corresponding to the bridge method ? You can see spring in org.springframework.core.BridgeMethodResolver Class source code . In fact, it is by judging the method name 、 The number of parameters and generic type parameters .

Link to original :https://blog.csdn.net/mhmyqn/article/details/47342577

5.1.3 Heap pollution

Heap pollution( Heap pollution ), It refers to when assigning an object without generics to a variable with generics , Heap pollution may occur .

and Heap Pollution It may lead to more serious consequences : ClassCastException, We were just varagMethod Add a few lines of code to the method as follows :

public static void varagMethod(Set<Integer> objects) {

        objects.add(new Integer(10));

}

And then modify main Method :

public static void main(String[] args){

        Set set = new TreeSet();

        varagMethod(set);

        Iterator<String> iter = set.iterator();

        while (iter.hasNext())
        {
            String str = iter.next();   // ClassCastException thrown

            System.out.println(str);
        }
}

 

You can see , The program reported one ClassCastException( Type conversion error ), This is because Java Allow us to put a generic parameter free Set Object passed to varagMethod Method , And we are varagMethod In the method , To the incoming Set Object added a Integer object , Then we traverse set When the collection , take Set The first element in is strongly transformed into String, Obviously Integer Can't be converted into String object

5.2 Translate generic expressions

Pair<Parson> pair=new Pair<Parson>();

pair.getFirst();

erase getFirst Will return... After the return type of Object type . The compiler automatically inserts Employee Cast of .

The compiler translates this method call into two virtual machine instructions :

  1. For the original method Pair.getFirst Call to .
  2. Will return Object Type cast to Parson type .

5.3 Subtype rule

5.3.1 Type boundary

Generic T In the end, it will be erased as Object type , Only use Object Methods .

5.3.2 wildcard

Unbounded wildcards

In the generic example above , We all specify specific types , At least also Object , Suppose there is a scenario , You don't know what this type is , It can be Object , It can also be Person To do that ? Wildcards are needed in this scenario , As shown below , Usually a To express

public void addAll(Collection<?> col){

...

}

Upper bound wildcard

Based on the above scenario , I want to limit this type to Person Subclasses of , As long as it is Person All subclasses of can , If generics are written as <Person> Then you can only force the following , Then the meaning of generics is lost , Back to the original starting point .

In view of this situation, there is the introduction of bounded wildcards . Specifying the upper boundary in a generic type is called the upper boundary wildcard <? extends XXX>.

 

Upper bound wildcard , Not by itself ,

 

The upper bound is used for the input parameters of the method : The input subclass can

 Add Subclass , Upper bound container , Can only get You can't add

 

Lower bound wildcard

The principle is the same as the upper bound wildcard , Lower bound Wildcards restrict unknown types to specific types or the type Of super type , Lower limit wildcards use wildcards (‘?’) Express , Heel super keyword , Lower limit of heel :<? super A> .

Empathy , Methods into the reference , The input parameter is a parent class, which can , only get No add

6 Limitations of generics

jdk Defined 7 Restrictions on the use of generic types :

1、 Generic instances cannot be instantiated with simple types

class Pair<K, V> {

private K key; private V value;

public Pair(K key, V value) {

this.key = key; this.value = value;

}

public static void main(String[] args) {

// Error will be reported at compile time , because int、char It belongs to the basic type , Cannot be used to instantiate generic objects 

Pair<int,char> p = new Pair(8,'a');

// Compile without error 

Pair<Integer,String> p2 = new Pair<>(8,"a");

} }

2、 You cannot directly create type parameter instances

public static <E> void append(List<E> list) {

E elem = new E(); // compile-time error list.add(elem);

}

// As a solution , You can create by reflection 

public static <E> void append(List<E> list, Class<E> cls) throws Exception {

E elem = cls.newInstance(); // OK

list.add(elem);

}

3、 Cannot declare a static attribute as a generic type parameter

public class MobileDevice<T> { 
    // illegal  
    private static T os; 
    //... 
}

4、 Cannot use cast or instanceof

 because java  The compiler will do type erasure in the compiler , Therefore, the type of parameters cannot be verified at run time 

public static <E> void rtti(List<E> list) { 
// An exception will be prompted at compile time  
if (list instanceof ArrayList<Integer>) { 
// ... 
} 
}

 The solution can be parameterized by unbounded wildcards  

public static void rtti(List<?> list) { 
// Compile without error  
if (list instanceof ArrayList<?>) { // ... } }

 In some cases , The compiler knows that type parameters are always valid , At this time, forced rotation is allowed 
 In some cases , The compiler knows that type parameters are always valid , At this time, forced rotation is allowed  

List<String> l1 = ...; 
ArrayList<String> l2 = (ArrayList<String>)l1; // OK

5、 Cannot create array generics

6、 You can't create、catch、throw Parameterized type object

7、 Overloaded methods cannot have two methods of the same primitive type

7 Summary

7.1 Two dimensions :

  1. flag
  2. Inheritance

7.2 ArrayList in addAll Method

Upper bound wildcard , abstract list in addAll

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

 

原网站

版权声明
本文为[Mengqi D Xiaowu]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130545511367.html