1. Tutte le classi ereditanoObject

Tutte le classi in Java ereditano implicitamente la Objectclasse.

Analizzeremo cos'è l'ereditarietà e come funziona in Java nella ricerca Java Core. Per ora, considereremo un semplice fatto che ne consegue:

Un oggetto di qualsiasi classe può essere assegnato a una Objectvariabile. Esempio:

Codice Nota
Object o = new Scanner(System.in);
La ovariabile memorizza un riferimento a un Scanneroggetto
Object o = new String();
La ovariabile memorizza un riferimento a un Stringoggetto
Object o = new Integer(15);
La ovariabile memorizza un riferimento a un Integeroggetto
Object o = "Hello";
La ovariabile memorizza un riferimento a un Stringoggetto

È qui che finisce la buona notizia. Il compilatore non tiene traccia del tipo originale di oggetto salvato in una Objectvariabile, quindi non sarai in grado di chiamare metodi sull'oggetto salvato diversi dai metodi della Objectclasse.

Se devi chiamare i metodi associati al tipo originale dell'oggetto, devi prima salvare un riferimento ad esso in una variabile del tipo corretto, quindi chiamare i metodi su quella variabile:

Codice Nota
Object o = new Scanner(System.in);
int x = o.nextInt();
Il programma non verrà compilato. La Objectclasse non ha nextInt()metodo.
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Questo funzionerà.

Qui salviamo un riferimento a un Scanneroggetto in una Scannervariabile usando un operatore typecast .

Non puoi semplicemente andare e assegnare una Objectvariabile a una variabile Scanner, anche se la Objectvariabile memorizza un riferimento a un Scanneroggetto. Ma puoi farlo se usi l' operatore typecast , che già conosci. Questo è il suo aspetto generale:

Type name1 = (Type) name2;

Dove name1è il nome di una Typevariabile ed name2è il nome di una Objectvariabile che memorizza un riferimento a un Typeoggetto.

Tipografia

Se il tipo di variabile e il tipo di oggetto non corrispondono, ClassCastExceptionverrà lanciato a. Esempio:

Codice Nota
Object o = new Integer(5);
String s = (String) o;
Si verificherà un errore in fase di esecuzione:
a ClassCastExceptionverrà lanciato qui

C'è un modo per evitare questo errore in Java: lo facciamo controllando il tipo dell'oggetto memorizzato in una variabile :

name instanceof Type

L' instanceofoperatore controlla se la namevariabile è un Typeoggetto.

Ad esempio, troviamo una stringa in un array di oggetti diversi:

Codice 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à questi valori rispettivamente in Integer, Stringe Double.

Passa sopra l'array di oggetti

Se l'oggetto è un String

Salvalo in una Stringvariabile
Visualizza la variabile sullo schermo.


2. Perché sono apparsi i generici: le raccolte

Torniamo alle collezioni.

Non appena gli sviluppatori Java hanno creato la ArrayListclasse, hanno voluto renderla universale, in modo che potesse memorizzare qualsiasi tipo di oggetto. Quindi hanno usato un array di Objects per memorizzare gli elementi.

Il punto di forza di questo approccio è che puoi aggiungere un oggetto di qualsiasi tipo alla collezione.

Naturalmente, ci sono diversi punti deboli.

Svantaggio 1.

Era sempre necessario scrivere un operatore di conversione del tipo durante il recupero di elementi da una raccolta:

Codice 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);
}
Crea una raccolta per memorizzare i riferimenti agli Objectoggetti

Riempi la raccolta con i numeri 10, 20, ... 100;



Somma gli elementi della collezione


Il typecasting è necessario

Svantaggio 2.

Non c'era alcuna garanzia che una raccolta contenesse un tipo specifico di elemento

Codice 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);
}
Creare una collezione per memorizzare i riferimenti agli Objectoggetti

Riempiamo la collezione con i numeri rappresentati come Doubleoggetti:
0.0, 2.5, 5.0, ...


Somma gli elementi della collezione


Ci sarà un errore: a Doublecannot be cast to anInteger

I dati possono essere inseriti nella raccolta ovunque:

  • in un altro metodo
  • in un altro programma
  • da un file
  • sulla rete

Svantaggio 3.

I dati nella raccolta possono essere modificati accidentalmente.

Puoi passare una raccolta piena dei tuoi dati a un metodo. Quel metodo, scritto da un programmatore diverso, aggiunge i suoi dati alla tua raccolta.

Il nome della raccolta non indica chiaramente quali tipi di dati possono essere archiviati in essa. E anche se dai alla tua variabile un nome chiaro, un riferimento ad essa può essere passato a una dozzina di metodi, e quei metodi sicuramente non sapranno nulla del nome originale della variabile.


3. Generici

Generici in Java

In Java, tutti questi problemi vengono eliminati da questa cosa interessante chiamata generici.

In Java, generici significa la possibilità di aggiungere parametri di tipo ai tipi. Il risultato è un tipo composito complesso. La visione generale di un tale tipo composito è questa:

ClassName<TypeParameter>

Questa è una classe generica. E può essere utilizzato ovunque si utilizzino normalmente le classi.

Codice Descrizione
ArrayList<Integer> list;
Creazione di variabili
list = new ArrayList<Integer> ();
Creare oggetti
ArrayList<Integer>[] array;
Creazione di array

Solo Integerle variabili possono essere memorizzate in una tale raccolta:

Codice Descrizione
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListraccolta con Integerelementi
Questo è consentito
E anche questo funzionerà
Autoboxing

Ma questo non è consentito: errore di compilazione

Imparerai come creare le tue classi con parametri di tipo nella quest Java Collections. Per ora, vedremo come usarli e come funzionano.


4. Come funzionano i generici

In realtà, i generici sono terribilmente primitivi.

Il compilatore sostituisce semplicemente i tipi generici con tipi ordinari. Ma quando vengono utilizzati metodi di un tipo generico, il compilatore aggiunge un operatore typecast per trasmettere i parametri ai parametri di tipo:

Codice Cosa fa il compilatore
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);

Supponiamo di avere un metodo che somma i numeri in una raccolta di numeri interi:

Codice Cosa fa il compilatore
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;
}

In altre parole, i generici sono una specie di zucchero sintattico, proprio come l'autoboxing, ma un po' di più. Con l'autoboxing, il compilatore aggiunge metodi per convertire an intin an Integere viceversa, e per i generici aggiunge operatori typecast.

Dopo che il compilatore ha compilato le classi generiche con parametri di tipo, vengono semplicemente convertite in classi ordinarie e operatori di typecast. Le informazioni sugli argomenti di tipo passati alle variabili di tipi generici vengono perse. Questo effetto è anche chiamato cancellazione del tipo .

A volte i programmatori che scrivono classi generiche (classi con parametri di tipo) hanno davvero bisogno delle informazioni sui tipi passati come argomenti. Nella ricerca Java Collections, imparerai come affrontare questo problema e cosa comporta.



5. Alcuni fatti sui generici

Ecco alcuni fatti più interessanti sui generici.

Le classi possono avere diversi parametri di tipo. Assomiglia a questo:

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

In realtà, questo non è davvero sorprendente. Ovunque il compilatore possa aggiungere un operatore per eseguire il cast a un tipo, può aggiungere più operatori di typecast.

Esempi:

Codice Nota
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
Il putprimo parametro del metodo è an Integere il secondo è aString

I tipi generici possono essere utilizzati anche come parametri . Assomiglia a questo:

ClassName<TypeParameter<TypeParameterParameter>>

Supponiamo di voler creare un elenco che memorizzerà elenchi di stringhe. In questo caso, otterremo qualcosa del genere:

// 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);

I tipi generici (tipi con parametri di tipo) possono essere utilizzati anche come tipi di matrice. Assomiglia a questo:

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

Non sta accadendo nulla di magico qui: le parentesi angolari indicano solo il nome del tipo:

Codice Controparte non generica
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];