1. Proponiendo el concepto de genéricos (¿por qué se necesitan genéricos)?
Primero, veamos el siguiente código corto:
public class genericTest {public static void main (string [] args) {list list = new ArrayList (); list.add ("Qqyumidi"); list.add ("maíz"); list.add (100); for (int i = 0; i <list.size (); i ++) {string name = (string) list.get (i); // 1 System.out.println ("Nombre:" + nombre); }}}Defina una colección del tipo de lista, primero agregue dos valores de tipo de cadena y luego agregue un valor de tipo entero. Esto está completamente permitido porque el tipo de lista predeterminado es objeto en este momento. En el bucle posterior, es fácil tener errores similares a // 1 porque olvidé agregar valores de tipo entero u otras razones de codificación en la lista anterior. Debido a que la etapa de compilación es normal, la excepción "java.lang.classcastException" aparecerá en tiempo de ejecución. Por lo tanto, tales errores son difíciles de detectar durante el proceso de codificación.
Durante el proceso de codificación como se indicó, encontramos que hay dos problemas principales:
1. Cuando colocamos un objeto en la colección, la colección no recordará el tipo de este objeto. Cuando este objeto se saca nuevamente de la colección, el tipo de compilación del objeto cambiado se convierte en el tipo de objeto, pero su tipo de tiempo de ejecución sigue siendo su propio tipo.
2. Por lo tanto, al sacar el elemento de colección AT // 1, los tipos forzados artificialmente deben convertirse en el tipo de objetivo específico, y es fácil ver la excepción "java.lang.classcastException".
Entonces, ¿hay alguna forma de hacer que la colección recuerde varios tipos de elementos en la colección, y lograrlo, siempre que no haya ningún problema durante la compilación, no habrá excepción de "java.lang.classcastException"? La respuesta es usar genéricos.
2. ¿Qué es un genérico?
Genéricos, es decir, "tipo parametrizado". Cuando se trata de parámetros, lo más familiar es tener parámetros concretos al definir un método y luego pasar los parámetros reales al llamar a este método. Entonces, ¿cómo entiendes el tipo de parametrización? Como su nombre indica, significa parametrizar el tipo del tipo específico original, similar a los parámetros variables en el método. En este momento, el tipo también se define como una forma de parámetro (se puede llamar un parámetro formal de tipo), y luego el tipo específico (tipo de parámetro real) se pasa cuando se usa/invoca.
Parece un poco complicado. Primero, echemos un vistazo al ejemplo anterior usando la escritura genérica.
public class genericTest {public static void main (string [] args) { /* list list = new ArrayList (); list.add ("Qqyumidi"); list.add ("maíz"); list.add (100); */ List <string> list = new ArrayList <String> (); list.add ("Qqyumidi"); list.add ("maíz"); //list.add(100); // 1 solicita un error de compilación para (int i = 0; i <list.size (); i ++) {string name = list.get (i); // 2 System.out.println ("Nombre:" + nombre); }}}Después de usar la escritura genérica, se produce un error de compilación cuando desea agregar un objeto de tipo entero en // 1. A través de la lista <String>, se limita directamente que solo los elementos de tipo de cadena pueden estar contenidos en la colección de listas, por lo que no es necesario que el tipo de emisión en // 2, porque en este momento, la colección puede recordar la información de tipo del elemento, y el compilador puede confirmar que es tipo de cadena.
Combinando la definición genérica anterior, sabemos que en List <String>, la cadena es un parámetro de tipo, es decir, la interfaz de lista correspondiente debe contener parámetros formales de tipo. Además, el resultado de retorno del método get () es directamente este tipo de parámetro formal (es decir, el parámetro de tipo entrante correspondiente). Echemos un vistazo a la definición específica de la interfaz de la lista:
Lista de interfaz pública <E> extiende la colección <E> {int size (); boolean isEmpty (); booleano contiene (objeto o); Iterador <E> iterator (); Objeto [] toArray (); <t> t [] toArray (t [] a); boolean add (e e); boolean eliminar (objeto o); boolean contiene todo (colección <?> c); boolean addall (colección <? extiende e> c); boolean addall (int index, colección <? extiende e> c); boolean removeall (colección <?> c); Boolean Retrainall (colección <?> C); nulo claro (); booleano es igual (objeto o); int hashcode (); E get (int index); E set (int index, e elemento); void add (int index, e elemento); E eliminar (índice int); int indexOf (objeto o); int LastIndexof (objeto o); ListIterator <E> listIterator (); ListIterator <E> listIterator (INT índice); List <E> Sublist (int fromIndex, int toIndex);}Podemos ver que después de adoptar la definición genérica en la interfaz de la lista, E in <E> representa un parámetro formal de tipo, que puede recibir parámetros de tipo específicos. En esta definición de interfaz, donde aparece E, significa que se aceptan los mismos parámetros de tipo aceptados desde el exterior.
Naturalmente, ArrayList es una clase de implementación para la interfaz de la lista, y su formulario de definición es:
Public Class ArrayList <E> extiende AbstractList <E> Lista de implementos <E>, RandomAccess, Clonable, java.io.serializable {public boolean add (e e) {asurecapacityInternal (tamaño + 1); // incrementa modcount! elementData [size ++] = e; devolver verdadero; } public e get (int index) {rangeCheck (index); checkforcomodification (); return arrayList.this.ElementData (offset + índice); } //...Omit Otros procesos de definición específicos}A partir de esto, entendemos desde la perspectiva del código fuente por qué el objeto de tipo entero se compila incorrectamente en // 1, y el tipo obtenido AT // 2 es directamente el tipo de cadena.
3. Personalizar interfaces genéricas, clases genéricas y métodos genéricos
Del contenido anterior, todos han entendido el proceso de operación específico de genéricos. También se sabe que las interfaces, las clases y los métodos también se pueden definir utilizando genéricos y utilizar en consecuencia. Sí, cuando se usa específicamente, se puede dividir en interfaces genéricas, clases genéricas y métodos genéricos.
Las interfaces genéricas personalizadas, las clases genéricas y los métodos genéricos son similares a List and ArrayList en el código fuente de Java anterior. De la siguiente manera, observamos la definición de clase y método de clase genérica más simple:
public class genericTest {public static void main (string [] args) {box <string> name = new Box <String> ("maíz"); System.out.println ("Name:" + name.getData ()); }} Box de clase <T> {datos privados t; Public Box () {} Public Box (t data) {this.data = data; } public t getData () {return data; }}En el proceso de definición de interfaces genéricas, clases genéricas y métodos genéricos, nuestros parámetros comunes como T, E, K, V, etc. a menudo se usan para representar parámetros formales genéricos porque reciben parámetros de tipo pasados por uso externo. Entonces, para diferentes tipos de parámetros entrantes, ¿los tipos de las instancias de objetos correspondientes generan las mismas?
public class genericTest {public static void main (string [] args) {box <string> name = new Box <String> ("maíz"); Box <Integer> age = new Box <Integer> (712); System.out.println ("Nombre clase:" + name.getClass ()); // com.qqyumidi.box system.out.println ("clase de edad:" + age.getclass ()); // com.qqqyumidi.box system.out.println (name.getClass () == Age.getClass ()); // verdadero }}A partir de esto, encontramos que cuando se usa clases genéricas, aunque se pasan diferentes argumentos genéricos, no se generan diferentes tipos en el verdadero sentido. Solo hay una clase genérica que pasa en diferentes argumentos genéricos en la memoria, es decir, sigue siendo el tipo más básico original (cuadro en este ejemplo). Por supuesto, lógicamente, podemos entenderlo como múltiples tipos genéricos diferentes.
La razón es que el propósito del concepto de genéricos en Java es que solo funciona en la etapa de compilación de códigos. Durante el proceso de compilación, después de verificar correctamente los resultados genéricos, se borrará la información relevante de los genéricos. Es decir, el archivo de clase compilado con éxito no contiene ninguna información genérica. La información genérica no ingresará a la fase de tiempo de ejecución.
Esto se resume en una oración: los tipos genéricos se consideran lógicamente como múltiples tipos diferentes, y en realidad son los mismos tipos básicos.
Cuatro. Tipo de comodín
Después de la conclusión anterior, sabemos que el cuadro <número> y el cuadro <integer> son en realidad ambos tipos de cuadros. Ahora necesitamos continuar explorando una pregunta. Entonces, lógicamente, ¿se pueden considerar el cuadro <número> y el cuadro <integer> como los tipos genéricos con las relaciones entre padres e hijos?
Para aclarar este problema, continuemos mirando el siguiente ejemplo:
public class genericTest {public static void main (string [] args) {box <número> name = new Box <Number> (99); Box <Integer> age = new Box <Integer> (712); getData (nombre); // El método getData (cuadro <número>) en el tipo genericest es // no aplicable para los argumentos (box <integer>) getData (edad); // 1} public static void getData (Box <Number> Data) {System.out.println ("Data:" + data.getData ()); }}Descubrimos que apareció un mensaje de error en el código // 1: el método getData (cuadro <número>) en el t Ype genericest no es aplicable para los argumentos (casilla <integer>). Obviamente, al solicitar información, sabemos que el cuadro <número> no puede considerarse lógicamente como la clase principal de cuadro <integer>. Entonces, ¿cuál es la razón?
public class genericTest {public static void main (string [] args) {box <integer> a = new Box <integer> (712); Box <Number> B = A; // 1 caja <float> f = nueva caja <float> (3.14f); B.SetData (f); // 2} public static void getData (Box <Number> Data) {System.out.println ("Data:" + data.getData ()); }} Box de clase <T> {datos privados t; Public Box () {} Public Box (t data) {setData (data); } public t getData () {return data; } public void setData (t data) {this.data = data; }}En este ejemplo, habrá un mensaje de error en // 1 y // 2. Aquí podemos usar el método contraproducente para explicarlo.
Suponiendo que el cuadro <número> se puede considerar lógicamente como la clase principal de cuadro <integer>, entonces no habrá indicaciones de error en // 1 y // 2. Entonces surge el problema. ¿Qué tipo es al obtener datos a través del método getData ()? ¿Entero? ¿Flotar? o número? Además, debido al orden incontrolable en el proceso de programación, el juicio de tipo debe hacerse cuando sea necesario y se realiza la conversión de tipo. Obviamente, esto contradice la idea de genéricos, por lo que lógicamente, el cuadro <número> no puede considerarse como la clase principal de caja <integer>.
Ok, luego veamos hacia atrás en el primer ejemplo en "Escriba comodines", conocemos la razón más profunda de sus indicaciones de error específicas. Entonces, ¿cómo resolverlo? La sede puede definir una nueva función. Obviamente, esto es contrario al concepto de polimorfismo en Java, por lo que necesitamos un tipo de referencia que pueda usarse lógicamente para representar la clase principal de la caja <integer> y el cuadro <número>, y por lo tanto, el tipo de comodín surgió.
Los comodines de tipo se usan generalmente en lugar de argumentos de tipo específicos. ¡Tenga en cuenta que este es un parámetro de tipo, no un parámetro de tipo! Y el cuadro <?> Es lógicamente la clase principal de todos los cuadros <integer>, cuadro <number> ..., etc. Por lo tanto, aún podemos definir métodos genéricos para cumplir con dichos requisitos.
public class genericTest {public static void main (string [] args) {box <string> name = new Box <String> ("maíz"); Box <Integer> age = new Box <Integer> (712); Box <Number> Number = nuevo cuadro <número> (314); getData (nombre); getData (edad); getData (número); } public static void getData (box <?> data) {system.out.println ("data:" + data.getData ()); }}A veces, también podemos escuchar sobre los tipos superiores e inferiores de comodines. ¿Cómo es exactamente?
En el ejemplo anterior, si necesita definir un método que funcione similar a getData (), pero hay restricciones adicionales a los argumentos de tipo: solo puede ser la clase numérica y sus subclases. En este momento, se requiere el límite superior de los comodines de tipo.
public class genericTest {public static void main (string [] args) {box <string> name = new Box <String> ("maíz"); Box <Integer> age = new Box <Integer> (712); Box <Number> Number = nuevo cuadro <número> (314); getData (nombre); getData (edad); getData (número); // getUppernumberData (nombre); // 1 getuppernumberdata (edad); // 2 getUppernumberData (número); // 3} public static void getData (box <?> Data) {system.out.println ("data:" + data.getData ()); } public static void getUppernumberData (box <? Extends Number> Data) {System.out.println ("Data:" + data.getData ()); }}En este punto, obviamente, la llamada en el código // 1 aparecerá un mensaje de error, mientras que la llamada al // 2 // 3 será normal.
El límite superior de los comodines de tipo se define por la forma de la caja <? extiende el número>. En consecuencia, el límite inferior de los comodines de tipo es la forma de la caja <? Super Número>, y su significado es exactamente lo opuesto al límite superior de los comodines de tipo. No lo explicaré demasiado aquí.
5. Capítulo adicional
Los ejemplos en este artículo se citan principalmente para ilustrar algunas ideas en genéricos y no necesariamente tienen usabilidad práctica. Además, cuando se trata de genéricos, creo que lo que más usas está en la colección. De hecho, en el proceso de programación real, puede usar genéricos para simplificar el desarrollo y puede garantizar bien la calidad del código. Y una cosa a tener en cuenta es que no hay la llamada matriz genérica en Java.
Para los genéricos, lo más importante es comprender las ideas y propósitos detrás de ellas.
Lo anterior es el contenido completo de la serie de resumen de Java: Java Generics Explicaciones detalladas presentadas por el editor. Espero que sea útil para todos y apoye a Wulin.com más ~