Introduction and use of generics

1. Concept

Generics are a new feature of Java SE 1.5. The essence of generics is parameterized types. When defining a method, the formal parameters do not specify specific Java types. Instead, generics are used instead of types. When calling, the actual parameters are passed in specific types. Generics can be used when defining classes or interfaces, which reduce redundant code and improve code reusability through inheritance or implementation.

Java cannot create a specific type of generic array
List[] li2 = new ArrayList[];


1. Type parameter: Do not specify a specific type when defining the parameter, use generic wildcards

2. Type actual parameter: Pass in the specific type when calling type (type argument)

2. Characteristics

Generics are only valid during the compilation phase. When the javac compiler compiles a java file into a binary bytecode file recognized by the computer, it will first perform a type check and perform type erasure after passing the check. The JVM does not know the existence of generics, because generics have been processed into ordinary classes and methods during the compilation phase;

3. The role of generics

1.类型安全
编译时的强类型检查,对集合等容器限制存储元素的类型,编译时检查存储的元素是否
是容器允许存储的类型。把运行时期的问题提前到了编译期间。

2.消除强制类型转换
没有泛型时可以存任意类型,取值时就需要强制类型转换。加了泛型,统一了容器中的元素类型,消除代码中强转,避免了强转时可能会出行的类型转换异常

3.更好的代码复用性:
在框架设计时,类可以使用泛型类,通过继承或者实现,实现了所有的公共方法或者定
义接口公共响应类时使用泛型类,减少了冗余代码,提高了代码的复用性。

4、潜在的性能收益
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,Java系统开发人员会指定这些强制类型转换)插入生成的字节码中。但是更多类
型信息可用于编译器这一事实,为未来版本的JVM的优化带来可能。

4. Generic classes

Generic types are used in class definitions (generics can be used to define public response entities). If generic arguments are passed in, corresponding restrictions will be made based on the passed in generic arguments. At this time, the generic type will Play the limiting role it is supposed to play. If no generic type arguments are passed in, the type defined using generic methods or member variables in a generic class can be any type.

1. Define the format

访问修饰符 class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
    
    
     private 泛型标识 /*(成员变量类型)*/ var; ..... 
}

2. Generic specification in the case of inheritance

1.子类继承泛型类,可以都不用指定泛型类型

2.子类可以指定泛型,父类可以不用指定

3.父类指定泛型,子类可以不用指定
@Data
public class Test<T> {
    
    

    private final String name = "哈哈哈";

    private T getT(T t){
    
    
        return t;
    }

    static class MyTest extends Test<String>{
    
    

        public String getStr(){
    
    
            String nameStr = this.getName();//哈哈哈
            System.out.println(nameStr);
            return super.getT("Hello");
        }
    }

    public static void main(String[] args) {
    
    
        String str =new MyTest().getStr();
        System.out.println(str);//Hello
    }
}

Note:

1. Generic type parameters can only be reference data types, not basic data types.

2. The instanceof operation cannot be used on exact generic types. If the following operation is illegal, an error will occur during compilation.

if(ex_num instanceof Generic<Number>){   
}

5. Generic interface

Generic interfaces are often used in producers of various classes

1. Define the format


 访问修饰符 <泛型标识> 泛型标识 方法名(){
    
    

 }

2. When a class that implements a generic interface does not pass in generic arguments:

When the generic argument is not passed in, the definition is the same as the generic class. When declaring the class, the declaration of the generic needs to be added to the class. If the generic is not declared, the compiler will report an error.

/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,
* 编译器会报错:"cannot be referenced from a static context"
*/
class FruitGenerator<T> implements Generator<T>{
    
    
    @Override
    public T next() {
    
    
        return null;
    }
}

3. When a class that implements a generic interface passes in generic arguments:

When an implementation class implements a generic interface, if the generic type has been passed into the actual parameter type, all uses of the generic type must be replaced with the passed in actual parameter type.

/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Test<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Test接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Test<T>,MyTest<String>,getStr(T t);中的的T都要替换成传入的String类型。
*/
public interface Test<T> {
    
    

    /**
     * 获取T
     * @param t
     * @return
     */
    String getStr(T t);

     class MyTest<String> implements Test<String>{
    
    

        @Override
        public java.lang.String getStr(String string) {
    
    

            return (java.lang.String) string;
        }
    }

    static void main(String[] args) {
    
    
         //Hello-World
        System.out.println(new MyTest().getStr("Hello-World"));
    }

}

6. Generic methods

To use generic properties in a class, the class must be generic.
To use generics in a class, the generic must either be declared in the class, or the method must be a generic method (a generic method declares the generic in the method). type)

调用方法的时候指明泛型的具体类型
格式:
访问修饰符 <T> 返回值类型 方法名(形参列表){
    
    }

6.1 Generic methods in a class.
Multiple generics can be defined, which one is used or returned according to the need. The generic
attribute is not declared in the class, and the compilation error is Cannot resolve symbol 'E'.
Both generic classes and generic methods. The two do not affect each other. Even if the two declared generics are the same, the generic type of the generic method can be different from the T declared in the generic class. An
ordinary method in a generic class takes the parameter as the generic type of the class. When calling the method, it can Pass in the generic itself and its subclasses

. Note:
Static methods cannot access generics defined on the class. If the static method needs to use generics, the static method must also be a generic method.

@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("rawtypes")
public class DynamicProxy<T,E> implements Serializable {
    
    
    private static final long serialVersionUID = 6733947012338693363L;

    private String id;

    private String name;

    private T data;

    //泛型类中未声明 编译报错 Cannot resolve symbol 'E'
    private E e;

    public String getString(T e){
    
    
        return String.valueOf(e);
    }

    static class DynamicProxyChile extends DynamicProxy{
    
    
        private static final long serialVersionUID = 226641748393219745L;
    }

    @SuperBuilder
    @Data
    static class Iphone{
    
    

    }

    public static <T,K> K getStr(T t,K k){
    
    
        return k;
    }

    public <T> T getStr(T t){
    
    
        return t;
    }

    public static void main(String[] args) {
    
    
        String str = DynamicProxy.getStr("Hello World !","我爱我国");
        System.out.println(str);//我爱我国

        DynamicProxy dynamicProxy = DynamicProxy.getStr(DynamicProxy.builder().id("1").name("奥巴驴").build(),
                DynamicProxy.builder().id("2").name(null).build());
        System.out.println(dynamicProxy);//DynamicProxy(id=2, name=null, data=null, e=null)

        //泛型类中的普通方法入参为类的泛型,调用方法时可以传入泛型本身和他子类
        DynamicProxy<DynamicProxy, Integer> proxy = new DynamicProxy<>();
        String string = proxy.getString(DynamicProxyChile.builder().build());//DynamicProxy子类
        String proxyString = proxy.getString(proxy);//DynamicProxy本身

        Iphone str1 = proxy.getStr(Iphone.builder().build());
        System.out.println(str1);//DynamicProxy.Iphone()

    }
}

6.2 Generic methods and variable parameters

Variable parameters can be of the same type or of different types

@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("rawtypes")
public class DynamicProxy<T,E> implements Serializable {
    
    
    private static final long serialVersionUID = 6733947012338693363L;

    private String id;

    private String name;

    private T data;

    public static <T> T[] getStr(T... args){
    
    
        return args;
    }

    public static void main(String[] args) {
    
    
        Serializable[] str = getStr(DynamicProxy.builder().build(), "1", 123);
        //DynamicProxy(id=null, name=null, data=null)  1 123
        Arrays.stream(str).forEach(o -> System.out.println(o));

        Integer[] integers = getStr(1, 2, 3, 4, 5);
        //1 2 3 4 5
        Arrays.stream(integers).forEach(o -> System.out.println(o));
    }
}

7. Generic wildcards

1. Type wildcard

当具体类型不确定的时候,这个通配符就是 ? ,如:Generic<Integer>Generic<Number>Generic<?>
类型通配符一般是使用?代替具体的类型实参

public void showKeyValue1(Generic<Number> obj){
    
    
    Log.d("泛型测试","key value is " + obj.getKey());
}
Generic<Integer>不能被看作为`Generic<Number>的子类
同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的

E - Element      在集合中使用 或者 Exception 异常的意思。
T - Type         JavaK - Key          java中的键
V - Value        java中的值
N - Number       数值类型

two,? Unbounded wildcard

Unbounded wildcard represents an uncertain java type
generic List<?>. New elements cannot be added via add() before assignment, indicating that any type of collection assignment can be accepted. After assignment, elements cannot be added to it at will, but Can remove and clear. List<?> generally receives an external collection as a parameter, or returns a collection of specific element types.

Usage scenarios:

1. When writing methods that can be used using the functions provided in the Object class.
2. When code uses methods from a generic class that do not depend on type parameters.

	public static void printList(List<?> list) {
    
    
       for (Object o : list) {
    
    
               /* 方法体 */
       }
	}

3. Restrict wildcards

1. The upper limit wildcard <? extends E>

is used when relaxing restrictions on variables.
List<? extends A> represents a List collection that can hold instances of A and its subclasses (such as B and C).
The upper limit wildcard can only get values ​​from the structure but cannot put the value into the structure: the value type is itself because you don't know what specific elements are in the structure.
As follows: you can only get values ​​from the list, but you cannot set values ​​in the list.

//取值是Number类型
public static Double sum(List<? extends Number> list){
    
    
    final Double[] sum = {
    
    (double) 0};
    list.forEach(item ->{
    
    
        sum[0] += item.doubleValue() ;
    });
    return sum[0];
}

public static void main(String[] args) {
    
    
   
    System.out.println(Test.sum(Arrays.asList(1, 25, 34, 5)));//65.0

    System.out.println(Test.sum(Arrays.asList(1.2, 2.5, 34.9, 5.0)));//43.6
}

2. Lower limit wildcard <? super E>

List <? super Integer> list represents Integer itself or a collection of Integer's parent classes.
The upper limit wildcard can get or set values ​​in the structure: the value is Object,
because the lower limit wildcard is either itself or its parent class, then it is legal for us to add itself and its subclasses to the structure.

//取值是Object
public static Integer sum(List<? super Integer> list){
    
    
    final Integer[] sum = {
    
    0};
    list.forEach(o ->{
    
    
        sum[0] += (Integer) o;
    });

    /**
     * java.lang.UnsupportedOperationException
     * Arrays.asList()生产的List的add、remove方法时报异常,这是由Arrays.asList() 返回的是Arrays的内部类ArrayList,
     * 而不是java.util.ArrayList。Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList,
     * remove、add等方法AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。
     * java.util.ArrayList重新了这些方法而Arrays的内部类ArrayList没有重新,所以会抛出异常。
     */
    list = new ArrayList<>(list);
    list.add(100);
    System.out.println(list);//[1, 2, 3, 4, 5, 100]
    return sum[0];

}

public static void main(String[] args) {
    
    
    System.out.println(Test.sum(Arrays.asList(1, 2, 3, 4, 5)));//15
}

Defining lower bound classes is not allowed

泛型在编译时都会被擦除,T 所代表的是一个 Fruit 的超类,但是具体是哪个类却是在运行时被决定的,编译器为了类型安全,
只能做最大限度的包容,因此所有的 T 类型都会在编译器变为 Object。所以,写 <T super Fruit> 等同于写 <Object>,
因此不支持 <T super Xx>

Insert image description here

8. Generic erasure

When the javac compiler compiles a java file into a binary bytecode file recognized by the computer, it will first perform a type check, and then perform type erasure after passing the check. The JVM does not know the existence of generics, because generics have been deleted during the compilation stage. Processed into ordinary classes and methods;

Erase rules:

若泛型类型没有指定具体类型,用Object作为原始类型;
若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型;
   List<String> strings = new ArrayList<>();
//        strings.add("你好,世界");

        List<Integer> integers = new ArrayList<>();
//        integers.add(100);

        //空集合打印  新增元素后打印
        //true     false
        System.out.println(strings.hashCode() == integers.hashCode());
        //true     false
        System.out.println(strings.equals(integers));
        //java.util.ArrayList      java.util.ArrayList
        System.out.println(strings.getClass().getName());
        //java.util.ArrayList     java.util.ArrayList
        System.out.println(integers.getClass().getName());
        //true  true
        System.out.println(strings.getClass().getName().hashCode() == integers.getClass().getName().hashCode());

类加载器把 ArrayList.class文件加载进jvm之后,只会存在一份地址相同的ArrayListClass类,所以虽然编译时是List<String>List<Integer>,
但是运行时泛型擦除了,他们的class类(也就是getClass()方法获得的)都是同一个,在编译完成后都会被编译器擦除成了 ArrayList(地址也相同)。
只不过该Class类有两个实例对象l1和l2而已,两个实例对象的地址不同。

No generic type is specified. After the generic class is type-erased, the corresponding type is replaced with the Object type.

@Data
public class Test<T> implements Serializable {
    
    
    private static final long serialVersionUID = 157827475733995670L;

    private T object;

    public Test(T object) {
    
    
        this.object = object;
    }

    public static void main(String[] args) {
    
    
        Test<String> hello = new Test<>("hello");
        Class<? extends Test> aClass = hello.getClass();
        System.out.println(aClass.getName()); //com.cihai.sojourn.T11111111111.Test  

        //Field name serialVersionUID type:long
        //Field name object type:java.lang.Object  没有指定泛型类型,泛型类被类型擦除后,相应的类型被替换成 Object 类型
        for (Field declaredField : aClass.getDeclaredFields()) {
    
    
            System.out.println("Field name "+declaredField.getName()+" type:"+declaredField.getType().getName());
        }
    }
}

If the upper limit is specified, the type parameter is replaced by the type upper limit type.

@Data
public class Test<T extends Integer> implements Serializable {
    
    
    private static final long serialVersionUID = 157827475733995670L;

    private T object;

    public Test(T object) {
    
    
        this.object = object;
    }

    public void add(T object){
    
    
    }

    public static void main(String[] args) {
    
    
        Test<Integer> hello = new Test<>(666);
        Class<? extends Test> aClass = hello.getClass();
        System.out.println(aClass.getName());//com.cihai.sojourn.T11111111111.Test

        //Field name serialVersionUID type:long
        //Field name object type:java.lang.Integer   指定了上限如 <T extends Integer>则类型参数就被替换成类型上限 Integer类型
        for (Field declaredField : aClass.getDeclaredFields()) {
    
    
            System.out.println("Field name "+declaredField.getName()+" type:"+declaredField.getType().getName());
        }

        Method[] declaredMethods = aClass.getDeclaredMethods();
        // method:public void com.cihai.sojourn.T11111111111.Test.add(java.lang.Object)  //擦除后add() 这个方法对应的 Method 的签名应该是 Object.class
        for (Method declaredMethod : declaredMethods) {
    
    
            System.out.println(" method:"+ declaredMethod);
        }

        //反射中找到 add 对应的 Method getDeclaredMethod("add", Object.class)
        try {
    
    
            String add = aClass.getDeclaredMethod("add", Object.class).getName();
            System.out.println("add方法名" + add);
        } catch (NoSuchMethodException e) {
    
    
            throw new RuntimeException(e);
        }

    }

}

Guess you like

Origin blog.csdn.net/chat_bot/article/details/127791203