1. Todas las clases heredanObject
Todas las clases en Java heredan implícitamente la Object
clase.
Analizaremos qué es la herencia y cómo funciona en Java en la búsqueda de Java Core. Por ahora, consideraremos un hecho simple que se deriva de esto:
Un objeto de cualquier clase se puede asignar a una Object
variable. Ejemplo:
Código | Nota |
---|---|
|
La o variable almacena una referencia a un Scanner objeto. |
|
La o variable almacena una referencia a un String objeto. |
|
La o variable almacena una referencia a un Integer objeto. |
|
La o variable almacena una referencia a un String objeto. |
Aquí es donde terminan las buenas noticias. El compilador no realiza un seguimiento del tipo original de objeto guardado en una Object
variable, por lo que no podrá llamar a métodos en el objeto guardado que no sean los métodos de la Object
clase.
Si necesita llamar a los métodos asociados con el tipo original del objeto, primero debe guardar una referencia a él en una variable del tipo correcto y luego llamar a los métodos en esa variable:
Código | Nota |
---|---|
|
El programa no compilará. La Object clase no tiene nextInt() método. |
|
Esto funcionará. Aquí guardamos una referencia a un Scanner objeto en una Scanner variable usando un operador typecast . |
No puede simplemente ir y asignar una Object
variable a una variable de escáner, incluso si la Object
variable almacena una referencia a un Scanner
objeto. Pero puedes hacer esto si usas el operador typecast , que ya conoces. Este es su aspecto general:
Type name1 = (Type) name2;
Donde name1
es el nombre de una Type
variable y name2
es el nombre de una Object
variable que almacena una referencia a un Type
objeto.
encasillamiento
Si el tipo de variable y el tipo de objeto no coinciden, se ClassCastException
lanzará un. Ejemplo:
Código | Nota |
---|---|
|
Se producirá un error en el tiempo de ejecución: se lanzará un aquí ClassCastException |
Hay una forma de evitar este error en Java: lo hacemos comprobando el tipo de objeto almacenado en una variable :
name instanceof Type
El instanceof
operador comprueba si la name
variable es un Type
objeto.
Como ejemplo, busquemos una cadena en una matriz de diversos objetos:
Código | Nota |
---|---|
|
Autoboxing convertirá estos valores en Integer , String y Double , respectivamente. Recorra la matriz de objetos Si el objeto es un String Guárdelo en una String variable Muestre la variable en la pantalla. |
2. Por qué aparecieron los genéricos: colecciones
Volvamos a las colecciones.
Tan pronto como los desarrolladores de Java crearon la ArrayList
clase, quisieron hacerla universal, para que pudiera almacenar cualquier tipo de objeto. Entonces usaron una matriz de Object
s para almacenar los elementos.
La fuerza de este enfoque es que puede agregar un objeto de cualquier tipo a la colección.
Por supuesto, hay varias debilidades.
Desventaja 1.
Siempre fue necesario escribir un operador de conversión de tipos al recuperar elementos de una colección:
Código | Nota |
---|---|
|
Crear una colección para almacenar referencias a Object objetos Llenar la colección con números 10 , 20 , ... 100 ; Sumar los elementos de la colección Es necesario encasillar |
Desventaja 2.
No había garantía de que una colección contuviera un tipo específico de elemento.
Código | Nota |
---|---|
|
Crear una colección para almacenar referencias a Object objetos Llenamos la colección con números representados como Double objetos: 0.0 , 2.5 , 5.0 , ... Suma los elementos de la colección Habrá un error: a Double no se puede convertir a unInteger |
Los datos se pueden poner en la colección en cualquier lugar:
- en otro método
- en otro programa
- de un archivo
- sobre la red
Desventaja 3.
Los datos en la colección se pueden cambiar accidentalmente.
Puede pasar una colección llena de sus datos a algún método. Ese método, escrito por un programador diferente, agrega sus datos a su colección.
El nombre de la colección no indica claramente qué tipos de datos se pueden almacenar en ella. E incluso si le da a su variable un nombre claro, se puede pasar una referencia a una docena de métodos, y esos métodos definitivamente no sabrán nada sobre el nombre original de la variable.
3. Genéricos
En Java, todos estos problemas son eliminados por esta genial cosa llamada genéricos.
En Java, los genéricos significan la capacidad de agregar parámetros de tipo a los tipos. El resultado es un tipo compuesto complejo. La vista general de un tipo compuesto de este tipo es la siguiente:
ClassName<TypeParameter>
Esta es una clase genérica. Y se puede usar donde normalmente usas clases.
Código | Descripción |
---|---|
|
Creando variables |
|
Creando objetos |
|
Creando arreglos |
Solo Integer
las variables se pueden almacenar en dicha colección:
Código | Descripción |
---|---|
|
ArrayList colección con Integer elementos Esto está permitido Y esto también funcionará
autoboxeo
Pero esto no está permitido: error de compilación |
Aprenderá a crear sus propias clases con parámetros de tipo en la búsqueda de colecciones de Java. Por ahora, veremos cómo usarlos y cómo funcionan.
4. Cómo funcionan los genéricos
En realidad, los genéricos son terriblemente primitivos.
El compilador simplemente reemplaza los tipos genéricos con tipos ordinarios. Pero cuando se usan métodos de un tipo genérico, el compilador agrega un operador de conversión de tipo para convertir parámetros a los parámetros de tipo:
Código | Qué hace el compilador |
---|---|
|
|
|
|
|
|
|
|
Supongamos que tenemos un método que suma los números en una colección de enteros:
Código | Qué hace el compilador |
---|---|
|
|
En otras palabras, los genéricos son una especie de azúcar sintáctico, como el autoboxing, pero un poco más. Con el encuadre automático, el compilador agrega métodos para convertir an int
a an Integer
y viceversa, y para los genéricos agrega operadores de encasillamiento.
Después de que el compilador compila sus clases genéricas con parámetros de tipo, simplemente se convierten en clases ordinarias y operadores de conversión de tipos. Se pierde la información sobre los argumentos de tipo pasados a las variables de tipos genéricos. Este efecto también se denomina borrado de tipo .
A veces, los programadores que escriben clases genéricas (clases con parámetros de tipo) realmente necesitan la información sobre los tipos pasados como argumentos. En la búsqueda de colecciones de Java, aprenderá cómo lidiar con esto y lo que implica.
5. Algunos datos sobre los genéricos
Aquí hay algunos datos más interesantes sobre los genéricos.
Las clases pueden tener varios parámetros de tipo. Se ve algo como esto:
ClassName<TypeParameter1, TypeParameter2, TypeParameter3>
En realidad, esto no es realmente sorprendente. En cualquier lugar donde el compilador pueda agregar un operador para convertir a un tipo, puede agregar múltiples operadores de conversión de tipos.
Ejemplos:
Código | Nota |
---|---|
|
El put primer parámetro del método es un Integer , y el segundo es unString |
Los tipos genéricos también se pueden utilizar como parámetros . Se ve algo como esto:
ClassName<TypeParameter<TypeParameterParameter>>
Supongamos que queremos crear una lista que almacenará listas de cadenas. En este caso, obtendremos algo como esto:
// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");
// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");
// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);
Los tipos genéricos (tipos con parámetros de tipo) también se pueden usar como tipos de matriz. Se ve algo como esto:
ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];
Aquí no sucede nada mágico: los paréntesis angulares solo indican el nombre del tipo:
Código | Contraparte no genérica |
---|---|
|
|
|
|
|
|