1. Todas las clases heredanObject

Todas las clases en Java heredan implícitamente la Objectclase.

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 Objectvariable. Ejemplo:

Código Nota
Object o = new Scanner(System.in);
La ovariable almacena una referencia a un Scannerobjeto.
Object o = new String();
La ovariable almacena una referencia a un Stringobjeto.
Object o = new Integer(15);
La ovariable almacena una referencia a un Integerobjeto.
Object o = "Hello";
La ovariable almacena una referencia a un Stringobjeto.

Aquí es donde terminan las buenas noticias. El compilador no realiza un seguimiento del tipo original de objeto guardado en una Objectvariable, por lo que no podrá llamar a métodos en el objeto guardado que no sean los métodos de la Objectclase.

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
Object o = new Scanner(System.in);
int x = o.nextInt();
El programa no compilará. La Objectclase no tiene nextInt()método.
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Esto funcionará.

Aquí guardamos una referencia a un Scannerobjeto en una Scannervariable usando un operador typecast .

No puede simplemente ir y asignar una Objectvariable a una variable de escáner, incluso si la Objectvariable almacena una referencia a un Scannerobjeto. Pero puedes hacer esto si usas el operador typecast , que ya conoces. Este es su aspecto general:

Type name1 = (Type) name2;

Donde name1es el nombre de una Typevariable y name2es el nombre de una Objectvariable que almacena una referencia a un Typeobjeto.

encasillamiento

Si el tipo de variable y el tipo de objeto no coinciden, se ClassCastExceptionlanzará un. Ejemplo:

Código Nota
Object o = new Integer(5);
String s = (String) o;
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 instanceofoperador comprueba si la namevariable es un Typeobjeto.

Como ejemplo, busquemos una cadena en una matriz de diversos objetos:

Código Nota
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
Autoboxing convertirá estos valores en Integer, Stringy Double, respectivamente.

Recorra la matriz de objetos

Si el objeto es un String

Guárdelo en una Stringvariable
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 ArrayListclase, quisieron hacerla universal, para que pudiera almacenar cualquier tipo de objeto. Entonces usaron una matriz de Objects 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
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Crear una colección para almacenar referencias a Objectobjetos

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
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Crear una colección para almacenar referencias a Objectobjetos

Llenamos la colección con números representados como Doubleobjetos:
0.0, 2.5, 5.0, ...


Suma los elementos de la colección


Habrá un error: a Doubleno 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

Genéricos en Java

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
ArrayList<Integer> list;
Creando variables
list = new ArrayList<Integer> ();
Creando objetos
ArrayList<Integer>[] array;
Creando arreglos

Solo Integerlas variables se pueden almacenar en dicha colección:

Código Descripción
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListcolección con Integerelementos
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
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

Supongamos que tenemos un método que suma los números en una colección de enteros:

Código Qué hace el compilador
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

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 inta an Integery 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
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
El putprimer 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
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];