Genéricos
Limite los elementos en una colección a un tipo específico.
el término
Algunas notas:
Los tipos parametrizados y los tipos primitivos son compatibles entre sí
ArrayList Collection1 = new ArrayList <Integer> (); // pase, no WarningRArrayList <Integer> colección2 = new ArrayList (); // pase, hay advertencia
Los tipos parametrizados no consideran las relaciones de herencia de los parámetros de tipo
ArrayList <String> Collection3 = New ArrayList <SPET> (); // La compilación no pasa ArrayList <S Object> Collection4 = New ArrayList <String> (); // La compilación no pasa
pero
ArrayList Collection5 = new ArrayList <Integer> (); ArrayList <String> Collection6 = Collection5; // Compilado por
"?" Comodín
"?" significa cualquier tipo. Usa el "?" Carácter comodín para referirse a varios tipos parametrizados. Puede llamar a métodos que no están relacionados con la parametrización (como el método size ()), y no pueden llamar métodos relacionados con la parametrización (como el método add ())
Extensión comodín
Calificar el límite superior de los personajes comodín
ArrayList <? extiende el número> colección1 = new ArrayList <Integer> (); // Compilar por ArrayList <? extiende el número> colección2 = new ArrayList <String> (); // Compilar por no
Calificar el límite inferior de los personajes comodín
ArrayList <? Super Integer> Collection3 = New ArrayList <Number> (); // Compilar por ArrayList <? Super Integer> Collection4 = New ArrayList <String> (); // Compilar por no
Métodos genéricos personalizados
Funciones de plantilla de C ++
plantilla <class t> t add (t x, t y) {return (t) (x+y);}Los genéricos Java se implementan básicamente en el compilador, utilizado por el compilador para realizar verificaciones de tipo y juicios de tipo, y luego generar bytecodo no genérico ordinario. Esta técnica de implementación es "borrado".
Instancia de "borrar"
Los genéricos se proporcionan al compilador JAVAC para definir el tipo de entrada de la colección. Cuando el compilador compila una colección con la descripción del tipo, se eliminará la información de "tipo".
public class genericTest {public static void main (string [] args) {new GenericEest (). testType (); } public void testType () {ArrayList <Integer> Collection1 = new ArrayList <Integer> (); ArrayList <String> collection2 = new ArrayList <String> (); System.out.println (colección1.getclass () == colección2.getclass ()); // Los tipos de clase de los dos son los mismos, es decir, el Bytecode es el mismo sistema.out.println (colección2.getclass (). GetName ()); // La clase es java.util.arrayList, y no hay información de parámetros de tipo real}}Producción
verdadero
java.util.arraylist
Use la reflexión para omitir el compilador y agregar otros tipos de datos a una colección genérica.
Solo los tipos de referencia pueden usarse como parámetros reales para métodos genéricos:
public class genericTest {public static void main (string [] args) {swap (new String [] {"111", "222"}, 0,1); // compilar por // swap (new int [] {1,2}, 0,1); // compilar no compilarse porque int no es el tipo de referencia swap (nuevo entero [] {1,2}, 0,1); // compilar por}/*intercambie los elementos i-th y j de matriz a*/public static <t> void swap (t [] a, int i, int j) {t temp = a [i]; a [i] = a [j]; a [j] = temp; }}Pero tenga en cuenta que los tipos básicos a veces se pueden usar como parámetros reales, porque hay un embalaje automático y un boxeo. Ejemplo (compilado y aprobado):
public class genericTest {public static void main (string [] args) {new GenericEest (). testType (); int a = bigueone (3,5); // int y doble, obtenga el intercambio como número Número B = más grande (3,5.5); // string e int obtiene el intercambio como objeto objeto c = bigueone ("1", 2); } // return y de x, y public static <t> t bigueSone (t x, t y) {return y; }}Al mismo tiempo, este ejemplo también muestra que cuando los parámetros reales son inconsistentes, T toma la intersección, es decir, la primera clase principal común. Además, si usa el número B = BiggerOne (3,5.5); a la cadena C = BiggerOne (3,5.5); Luego se informa el error de compilación:
Error: (17, 29) Java: tipos incompatibles: tipos inferidos no cumplen con la inferencia del límite superior: java.lang.number & java.lang.comparable <? extiende java.lang.number & java.lang.comparable <? >>
Límite superior: java.lang.string, java.lang.object
Pero hay una cosa que no entendía. La depuración paso a paso en Idea y descubrí que el resultado es el siguiente: Captura de pantalla de depuración genérica-1 No sé por qué B es de tipo doble (pero compilará e informará un error al recibir el valor de retorno en el doble B). No sé si tiene algo que ver con el IDE. ¿El IDE muestra el tipo más preciso de este objeto al depurar?
Tipo de inferencia de parámetros de tipo
El proceso por el cual el compilador juzga los parámetros de tipo real de un método genérico se llama inferencia de tipo.
Cuando se aplica una determinada variable de tipo solo uno de todos los parámetros y los valores de retorno en toda la lista de parámetros, se determina en función del tipo de aplicación real en ese momento cuando se llama al método. Es decir, el tipo de parámetros genéricos se determina directamente en función del tipo de parámetro o el valor de retorno aprobado cuando se llama al método. Por ejemplo:
Swap (nueva cadena [3], 1,2) -> static <e> void swap (e [] a, int i, int j)
Cuando se aplica una variable de tipo en varios lugares en todos los parámetros y valores de retorno de toda la lista de parámetros, si tantos tipos de aplicaciones reales corresponden al mismo tipo al llamar al método, el tipo de parámetro genérico es ese tipo. Por ejemplo:
Agregar (3,5) -> estático <t> t add (t a, t b)
Cuando se aplica una determinada variable de tipo en muchos lugares en todos los parámetros y valores de retorno de toda la lista de parámetros, si la aplicación real tipos en tantos lugares corresponde a diferentes tipos al llamar al método y no hay valor de retorno, el tipo de intersección máxima entre los parámetros múltiples, es decir, la primera clase principal común. Por ejemplo:
relleno (nuevo entero [3], 3.5) -> estático <t> Void llenado (t a [], t v)
El tipo real correspondiente de este ejemplo es el número, los problemas compilados y de ejecución.
Cuando se aplica una variable de tipo en múltiples lugares en todos los parámetros y valores de retorno de toda la lista de parámetros, si los tipos de aplicación real en tantos lugares corresponden a diferentes tipos al llamar al método, y hay un valor de retorno, el tipo de valor de retorno se da prioridad, por ejemplo::
int x = add (3,3.5) -> static <t> t add (t a, t b)
El error de ejemplo anterior compilado, y el tipo X se cambia a Float también informó un error, y el cambio a número es exitoso.
Los ejemplos de inferencia de tipo de tipos de parámetros son transitivos:
Copy (nuevo entero [5], nueva cadena [5]) -> Copia static <t> void (t [] a, t [] b)
Este ejemplo infiere que el tipo de parámetro real es objeto y compilado.
Copy (New ArrayList <String>, nuevo entero [5]) -> Copia static <t> void (colección <t> a, t [] b)
Este ejemplo determina directamente la variable de tipo como tipo de cadena basado en la instancia de clase de ArrayList parametrizada e informa un error en la compilación.
Clases genéricas personalizadas
ejemplo
public class GenericDao <t> {public void add (t x) {} public t findByid (int id) {return null; } public void delete (t obj) {} public void delete (int id) {} public void Update (t obj) {} public t findByUsername (nombre de cadena) {return null; } public <t> set <t> findByConditions (string where) {return null; }}Nota: Cuando una variable se declara como genérica, solo se puede llamar por variables y métodos de instancia (y tipos integrados), pero no por variables estáticas y métodos estáticos. Debido a que los miembros estáticos son compartidos por clases parametrizadas, los miembros estáticos no deben tener parámetros de tipo de clase.
Comparación de métodos genéricos y clases genéricas
ejemplo:
Clase pública A <t> () {// Método de miembro de la clase genérica, la t está restringida por t siguiendo un public t memberfunc () {return null; } // Método genérico, aquí t y t son diferentes de t de la clase A pública estatic <t> t genicfunc (t a) {return null; } public static void main (string [] args) {// compilado sin pasar // integer i = a <tring> (). findByUsername ("s"); // compilado por set <Integer> set = a <tring> (). FindByConditions ("S"); }}Aquí entero i = a <tring> (). FindByUsername ("S"); compilará e informará un error:
Error: (35, 61) Java: Tipo incompatible: java.lang.string no se puede convertir a java.lang.integer
A partir de este ejemplo, se puede ver que la T del método genérico y la T de la Clase A son diferentes.
Genéricos y reflexiones
Obtenga los parámetros de tipo real de un genérico a través de la reflexión
Use variables genéricas como parámetros del método y use el método GetGenericParametertypes de la clase de método para obtener el ejemplo de parámetro de tipo real del genérico:
public class genericTest {public static void main (string [] args) lanza excepción {getParamType (); } /*Use la reflexión para obtener el tipo de parámetro real de parámetros de método* / public static void getParamType () lanza NosuchMethodException {método método = genericest.class.getMethod ("applymap", map.class); // Obtener el tipo de parámetros genéricos del tipo de método [] tipos = método.getGenericParametertyPes (); System.out.println (tipos [0]); // Tipo parametrizado ParameterizedType ptype = (parameterizedType) tipos [0]; // Tipo primitivo System.out.println (ptype.getrawtype ()); // Tipo real de parámetros System.out.println (ptype.getActualTyPearGuments () [0]); System.out.println (ptype.getActualTypeArGuments () [1]); } /* Método para probar tipos de parámetros* / public static void AplicleMap (map <integer, string> map) {}}Resultado de salida:
java.util.map <java.lang.integer, java.lang.string> interfaz java.util.mapclass java.lang.integlass java.lang.string