Camino de aprendizaje de Java genérico

Camino de aprendizaje de Java genérico

Visión general

¿Qué es genérico? Los genéricos de Java (genéricos) son una nueva característica introducida en JDK 5. Los genéricos proporcionan un mecanismo de detección de seguridad de tipos en tiempo de compilación, que permite a los programadores detectar tipos ilegales en tiempo de compilación.

La esencia de los genéricos es un tipo parametrizado, lo que significa que el tipo de datos que se manipula se especifica como parámetro.

La esencia de los genéricos es parametrizar tipos (en el caso de no crear un nuevo tipo, el tipo especificado por genéricos se usa para controlar los tipos restringidos específicos de parámetros formales) . Es decir, en el proceso de uso de genéricos, se designa como parámetro el tipo de datos de la operación. Este tipo de parámetro se puede usar en clases, interfaces y métodos, que se denominan clases genéricas, interfaces genéricas y métodos genéricos. , respectivamente.

Un método genérico

Los métodos genéricos pueden recibir diferentes tipos de parámetros cuando se llaman. El compilador manejará cada llamada al método de manera apropiada de acuerdo con el tipo de parámetros pasados ​​al método genérico.

Siga las siguientes reglas al definir métodos genéricos:

  • Los métodos genéricos deben declarar que hay una parte de declaración de parámetro de tipo (separada por paréntesis angulares), la parte de declaración de parámetro de tipo está antes del tipo de retorno del método;
  • Cada parte de declaración de parámetro de tipo contiene uno o más parámetros de tipo, separados por comas. Un parámetro genérico, también conocido como variable de tipo, es un identificador que se utiliza para especificar el nombre de un tipo genérico;
  • Los parámetros de tipo se pueden usar para declarar el tipo de valor de retorno y se pueden usar como un marcador de posición para el tipo de parámetro real obtenido por un método genérico;
  • Los parámetros de tipo solo pueden representar tipos de datos de referencia y no se pueden utilizar para tipos de datos básicos;
  • La declaración del cuerpo del método genérico es la misma que la de otros métodos.

Ejemplo:

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Integer[] a1 = {
    
    1, 2, 3};
        Double[] a2 = {
    
    1.0, 2.0, 3.0};
        Character[] a3 = {
    
    'a', 'b', 'c'};

        Demo.print(a1);  // 1 2 3
        Demo.print(a2);  // 1.0 2.0 3.0 
        Demo.print(a3);  // a b c
    }

    public static <E> E[] print(E[] input) {
    
    
        for (E i : input) {
    
    
            System.out.print(i + " ");
        }
        System.out.println();
        return input;  // 返回值也为泛型
    }
}

Dos, clase genérica

La parte de declaración de parámetro de tipo de una clase genérica también contiene uno o más parámetros de tipo, separados por comas.

Un parámetro genérico, también conocido como variable de tipo, es un identificador que se utiliza para especificar el nombre de un tipo genérico. Debido a que aceptan uno o más parámetros, estas clases se denominan clases parametrizadas o tipos parametrizados.

import java.util.Arrays;

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Integer[] a1 = {
    
    1, 2, 3};
        Box<Integer[]> b1 = new Box<>(a1);
        System.out.println(Arrays.toString(b1.getT()));

        Character[] a2 = {
    
    'a', 'b', 'c'};
        Box<Character[]> b2 = new Box<>(a2);
        System.out.println(Arrays.toString(b2.getT()));
    }
}

class Box<T> {
    
    
    private final T t;

    public Box(T t) {
    
    
        this.t = t;
    }

    public T getT() {
    
    
        return t;
    }
}

Tres, interfaz genérica

Implementar interfaz genérica también simplemente el nombre de la interfaz <类型名>en la línea.

Pero hay dos formas de implementar la clase de interfaz:

Si la subclase que implementa la interfaz no quiere usar declaraciones genéricas, simplemente especifique su tipo de operación específico directamente al implementar la interfaz. Por ejemplo MyClass1;

Si la subclase que implementa la interfaz desea utilizar declaraciones genéricas, utilice genéricos al implementar la interfaz. Por ejemplo MyClass2.

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        MyClass1 m1 = new MyClass1("Hello");
        System.out.println(m1.getValue());;

        MyClass2<Integer> m2 = new MyClass2<>(1);
        System.out.println(m2.getValue());;
    }
}

interface info<T> {
    
    
    T getValue();
}

// 指定具体的类
class MyClass1 implements info<String> {
    
    
    private final String value;

    public MyClass1(String value) {
    
    
        this.value = value;
    }

    @Override
    public String getValue() {
    
    
        return value;
    }
}

// 泛型类
class MyClass2<T> implements info<T> {
    
    
    private final T value;

    public MyClass2(T value) {
    
    
        this.value = value;
    }

    @Override
    public T getValue() {
    
    
        return value;
    }
}

Cuatro, comodín

Cuando usamos Fanhua, los genéricos alrededor del signo igual deben ser consistentes, pero es posible que no sepa cuál es el genérico en el lado derecho del signo igual. En este momento, Java nos proporciona comodines genéricos, que se pueden usar en el lado izquierdo del signo igual.

Antes de comprender los comodines, primero debemos comprender la relación entre las referencias y las instancias de Java:

En Java, una referencia es equivalente a un contenedor y una instancia es equivalente a agua en el contenedor. En circunstancias normales, el agua solo se puede llenar en un recipiente que sea igual o mayor que él (transformación ascendente: las instancias de subclases se cargan en el recipiente de la clase principal). Pero también se puede cargar en un contenedor más pequeño que él mismo (abatido: la instancia de la clase principal se carga en el contenedor de la subclase), pero este paso es muy peligroso y habrá muchas restricciones. El genérico es resolver el problema del abatimiento inseguro .

A continuación, usamos la relación entre el plato de fruta y la fruta para explicar los comodines:

Primero cree una clase de placa:

// 水果盘类
class Plate<T> {
    
    
    private T item;
    public Plate(T item) {
    
    
        this.item = item;
    }
    public void set(T item) {
    
    
        this.item = item;
    }
    public T get(){
    
    
        return item;
    }
}

Luego crea un poco más de fruta:

// 水果类
class Fruit {
    
    }

// 具体的水果
class Apple extends Fruit {
    
    }
class Banana extends Fruit {
    
    }

Comodín ilimitado

El uso de comodines ilimitados permite que los genéricos reciban cualquier tipo de datos. Es equivalente <? extends Object>a emparejar todos los tipos.

Plate<?> p1 = new Plate<>(new Fruit());
// p1.set(new Fruit());
// p1.set(new Apple());
Object p11 = p1.get();
// Fruit p12 = p1.get();
// Apple p13 = p1.get();

Plate<?> p2 = new Plate<>(new Apple());
// p2.set(new Fruit());
// p2.set(new Apple());
Object p21 = p1.get();
// Fruit p22 = p1.get();
// Apple p23 = p1.get();

El comodín ilimitado coincide con cualquier clase .

No puedes usar el setmétodo. Porque no sé qué tan grande es el recipiente dentro del plato. Si el recipiente dentro del plato es relativamente pequeño, causará problemas si pones algo grande en él, o si el recipiente dentro del plato no tiene nada que ver con lo que quieras poner, así que aún más habrá un problema;

Tampoco se puede colocar el resultado recuperado en ninguna clase que no sea la clase Object. Como no sabe qué tan grande es el contenido del plato, si el recipiente que sostiene no tiene nada que ver con el contenido, o el tamaño es más pequeño que el interior, entonces habrá problemas. Llame Objectpara recibir sin problema, porque es el contenedor más grande, no importa lo que ponga dentro que pueda acceder se mantiene.

El resumen es: no se puede guardar ni sacar.

Comodín de límite superior

Los genéricos que utilizan comodines con un límite superior fijo pueden recibir datos de un tipo específico y todas sus subclases. El tipo especificado aquí puede ser una clase o una interfaz.

Plate<? extends Fruit> p3 = new Plate<>(new Fruit());
// p3.set(new Fruit());
// p3.set(new Apple());
Fruit p31 = p3.get();
Object p32 = p3.get();
// Apple p33 = p3.get();

El carácter de límite superior puede coincidir con cualquier clase derivada o la clase misma con esta clase como clase principal .

No puede llamar al setmétodo. Debido a que la máquina virtual solo sabe que se coloca en la clase principal o una clase derivada en la clase principal, una clase no sabe exactamente cuál, si la original es una subclase, ponga el tiempo para poner la clase principal, tendrá un problema, por lo que no puede llamar al setmétodo ;

Pero puede llamar a getmétodos, sabiendo que dentro del contenedor está el padre o el padre de una clase derivada, así que tómese el tiempo para encontrar un contenedor siempre que el padre sea más que igual para ser instalado absolutamente.

El resumen es: no se puede guardar, solo sacar.

Comodín de límite inferior

El tipo genérico de todos los comodines con un límite inferior fijo puede recibir datos del tipo especificado y todos sus tipos de superclase.

Plate<? super Fruit> p4 = new Plate<>(new Fruit());
p4.set(new Fruit());
p4.set(new Apple());
// Fruit p41 = p4.get();
Object p42 = p4.get();
// Apple p43 = p4.get();

Puede llamar a setmétodos. Debido a que las máquinas virtuales saben, el plato es el contenedor más pequeño, la Fruitclase es tan grande, así que cuando lo coloque, coloque algo que sea menor o igual al Fruittamaño de la clase, no sería un problema;

Pero no se puede llamar al getmétodo, porque la máquina virtual no sabe cómo funcionan las cosas por dentro, si escoges el contenedor equivocado será más pequeño que el interior, pero si coges el Objectpick no habría problema, porque Objectese es el más grande del estado .

Principios de PECS

PECS (Producer amplía Consumer Super) 原则

  • Es adecuado utilizar el límite superior Extends si el contenido se lee con frecuencia .
  • A menudo insertado, adecuado para el límite inferior Super.

Supongo que te gusta

Origin blog.csdn.net/qq_43580193/article/details/112749414
Recomendado
Clasificación