Che cos'è Generics in Java?

Generics in Java è stato introdotto nel 2004 come una nuova funzionalità del linguaggio di programmazione Java e faceva parte della versione JDK 5. È ampiamente utilizzato insieme al framework delle raccolte Java. Ad oggi è una delle caratteristiche più importanti e ricercate del linguaggio di programmazione Java.

Java generico è stato trovato da quattro persone, Gilad Bracha, Martin Odersky, David Stoutamire e Philip Wadler nel 1998. Era un'estensione del linguaggio Java che supportava i tipi generici. Doveva raggiungere due obiettivi principali che sono:

  1. Digitare sicurezza
  2. Riutilizzabilità del codice

Definizione di generici in Java

I generici possono essere definiti come un modo per ottenere la riusabilità del codice definendo classi generiche, interfacce, costruttori e metodi che possono essere utilizzati con diversi tipi di dati e ottenere anche la sicurezza dei tipi dichiarando in anticipo il tipo di dati utilizzato nell'implementazione, eliminando quindi le possibilità di un errore di runtime.

Come vengono implementati i generici in Java?

I generici sono implementati usando parentesi angolari "". Le parentesi racchiudono il parametro del tipo "T" al loro interno. Esempio, . Il parametro del tipo "T" è un segnaposto che indica che un tipo di dati gli verrà assegnato in fase di esecuzione. Ad esempio, una classe generica verrà definita come:

public class MyGenericClass (…)

Di seguito sono riportati i parametri di tipo standard:

  • T: Tipo
  • E: elemento
  • N: Numero
  • K: Chiave
  • V: valore

S, U, V e così via vengono utilizzati per definire rispettivamente il secondo, il terzo e il quarto parametro nel caso in cui vengano utilizzati più parametri.

Comprensione di Generics in Java

Ormai potresti chiederti cos'è la sicurezza dei tipi e come funziona? O in che modo classi, interfacce, costruttori e metodi generici sono diversi dalle nostre classi e metodi regolari che li rendono riutilizzabili? Scopriamolo.

Essendo Java un linguaggio tipicamente statico, è necessario dichiarare il "tipo" che è il tipo di dati del valore trattenuto dalla variabile prima di usarlo.

Esempio: String myString =”eduCBA”;

Qui "String" è il tipo di dati, "myString" è la variabile che conterrà un valore il cui tipo è String.

Ora, se provi a passare un valore booleano al posto di una stringa, ad esempio:

String myBooleanStr = true;

Riceverai immediatamente un errore in fase di compilazione che indica "Tipo non corrispondente: impossibile convertire da booleano a stringa".

Come possiamo ottenere la riusabilità del codice con Generics?

Ora, definiamo un metodo regolare:

public static void welcome(String name)(
System.out.println("welcome to " + name);
)

Questo metodo può essere invocato solo passando un parametro stringa. Per esempio:

welcome(“eduCBA”);

Il suo output sarà "benvenuto in eduCBA".

Tuttavia, non è possibile invocare questo metodo ignorando altri tipi di dati come interi o booleani. Se si tenta di farlo, verrà richiesto un errore di compilazione che indica "Il metodo welcome (String) nel tipo Runner non è applicabile per gli argomenti (booleano)". Ciò significa che non è possibile passare nessun altro tipo di dati a un metodo che accetta solo una stringa come parametro.

Ciò significa anche che se si desidera invocare un metodo simile per un diverso tipo di dati, sarà necessario scrivere un nuovo metodo che accetta il tipo di dati richiesto come parametro. Questa caratteristica dei metodi di riscrittura con parametri di diversi tipi di dati è anche nota come metodo di sovraccarico. Il principale svantaggio di questo è che aumenta la dimensione del codice.

Tuttavia, potremmo anche utilizzare Generics per riscrivere il metodo sopra riportato e utilizzarlo per qualsiasi tipo di dati richiesto.

Definizione di un metodo generico:

public static void welcome(T t)(
System.out.println("it is " + t);
)

Nota : qui "t" è un oggetto di tipo T. A T verrà assegnato il tipo di dati utilizzato per invocare il metodo.

Ora puoi riutilizzare questo metodo invocandolo per una stringa quando richiesto o un valore booleano o intero o qualsiasi altro tipo di dati.

welcome("educate");
Integer Myint = 1;
welcome(Myint)
welcome(true);

Le dichiarazioni precedenti forniranno l'output seguente:

È Educa
È 1
Questo è vero

Pertanto, utilizzando generics qui siamo in grado di riutilizzare il nostro metodo per diversi tipi di dati.

Come possiamo ottenere la sicurezza dei tipi usando Generics?

Una delle principali differenze tra array e raccolta è che gli array possono archiviare solo dati omogenei, mentre le raccolte possono archiviare dati eterogenei. Ovvero, le Collezioni possono archiviare qualsiasi tipo / oggetto di dati definito dall'utente.

NOTA: le raccolte possono contenere solo oggetti (tipo di dati definito dall'utente) e non un tipo di dati primitivo. Per lavorare con dati primitivi, le raccolte di tipi utilizzano le classi wrapper.

Consideriamo ora un ArrayList.

ArrayList myList = new ArrayList();

Aggiungiamo dati di tipo String, Integer e Double all'oggetto ArrayList.

myList.add("eduCBA");
myList.add(1);
myList.add(5.2);

Sulla stampa dell'oggetto ArrayList possiamo vedere che contiene i seguenti valori: (eduCBA, 1, 5.2).

Ora, se si desidera recuperare questi valori in variabili, è necessario digitarli.

String someStr = (String)myList.get(0);
Integer someInt = (Integer)myList.get(1);
Double someFlt = (Double)myList.get(2);

Nel caso in cui non si esegua il typecast, verrà richiesto un errore in fase di compilazione che indica "Tipo non corrispondente: impossibile convertire da oggetto a stringa".

Da questo, puoi concludere che durante il recupero degli oggetti dalla tua ArrayList, devi tipografarlo nei rispettivi tipi. La domanda che si pone qui è come saprai a quale tipo di dati tipografarlo? In tempo reale ArrayList conterrà migliaia di record e la loro tipizzazione in diversi tipi di dati per ogni singolo oggetto non sarà un'opzione. Si potrebbe finire per digitarlo sul tipo di dati errato. Cosa succede allora?

Questa volta non verrà visualizzato un errore di compilazione ma verrà generato un errore di runtime che indica "Eccezione nel thread" principale "java.lang.ClassCastException: java.lang.Integer non può essere eseguito il cast su java.lang.String su com.serviceClasess.Runner .main (Runner.java:43)”.

Poiché non possiamo garantire il tipo di dati presenti all'interno di una raccolta (in questo caso ArrayList), non sono considerati sicuri da usare rispetto al tipo. È qui che entrano in gioco i generici per garantire la sicurezza dei tipi.

Utilizzando ArrayList con Generics:

ArrayList myList = new ArrayList();

Si noti che all'interno delle parentesi angolari "", viene specificato il tipo di stringa, il che significa che questa particolare implementazione di ArrayList può contenere solo dati di tipo stringa. Se si tenta di aggiungere qualsiasi altro tipo di dati, genererà semplicemente un errore di tempo di compilazione. Qui hai reso il tuo ArrayList sicuro, eliminando la possibilità di aggiungere un tipo di dati diverso da "String".

Ora che hai specificato il tipo di dati che è possibile aggiungere alla tua raccolta con l'aiuto di generici, non è più necessario digitarlo durante il recupero dei dati. Cioè puoi semplicemente recuperare i tuoi dati scrivendo:

String someStr = myList.get(0);

In che modo Generics in Java rende il lavoro così semplice?

Aiuta a rendere sicure le tue raccolte in modo da garantire in tal modo che il codice non fallisca in un secondo momento a causa di un'eccezione di runtime. Inoltre, consente al programmatore di dover eseguire il cast di ogni oggetto della raccolta, rendendo lo sviluppo del codice più rapido e semplice. Facendo uso di classi e metodi generici si può anche riutilizzare il codice secondo i tipi di dati richiesti durante l'implementazione.

Cos'altro puoi fare con Generics in Java?

Finora abbiamo visto come possiamo ottenere la sicurezza dei tipi e la riusabilità del codice con i generici. Ora diamo un'occhiata alle altre funzionalità fornite da generics. Loro sono:

  1. Tipi associati e multipli
  2. Digitare i caratteri jolly

Tipo associato: nel caso di un tipo limitato, il tipo di dati di un parametro è limitato a un intervallo particolare. Ciò si ottiene con l'aiuto della parola chiave "estende".

Ad esempio, consideriamo una classe generica con un parametro di tipo limitato che estende l'interfaccia Runnable:

class myGenericClass()

Ora, durante la creazione del suo oggetto in un'altra classe:

myGenericClass myGen = new myGenericClass();

L'istruzione precedente verrà eseguita perfettamente senza errori. Cioè nel caso del tipo limitato puoi passare lo stesso tipo di classe o il suo tipo di classe figlio. Inoltre, puoi associare il tipo di parametro a un'interfaccia e passarne le implementazioni quando lo invochi, come nel caso del nostro esempio sopra.

Cosa succede se si tenta di utilizzare qualsiasi altro tipo di parametro?

myGenericClass myGen = new myGenericClass();

Nel caso precedente, si verificherà un errore di compilazione in cui si afferma "Mancata corrispondenza del tipo: il tipo Integer non è un sostituto valido per il typecast del tipo myGenericClass".

Tipi con limiti multipli: in caso di tipi con limiti multipli possiamo associare il tipo di dati dei parametri a più di un tipo. Per esempio,

Class myGeneric()

In questo caso, è possibile passare qualsiasi tipo che estende la classe Number e implementa l'interfaccia Runnable. Tuttavia, quando si utilizzano più tipi limitati, è necessario tenere presente alcune cose:

  1. Non possiamo estendere più di una classe alla volta.
  2. Siamo in grado di estendere un numero qualsiasi di interfacce in un momento che non vi è alcun limite per le interfacce.
  3. Il nome della classe dovrebbe sempre venire prima seguito dal nome dell'interfaccia, altrimenti si verificherà un errore di compilazione.

Digitare i caratteri jolly: sono rappresentati da "?" - simbolo del punto interrogativo. Utilizza due parole chiave principali:

extends (per definire il limite superiore) e super (per definire i limiti inferiori).

Per esempio,

ArrayList al

Questo oggetto ArrayList "al" conterrà tutti i dati di tipo T e tutte le sue sottoclassi.

ArrayList al

Questo oggetto ArrayList "al" conterrà tutti i dati di tipo T e tutte le sue superclassi.

Vantaggi di Generics in Java

1. Flessibilità : Generics offre al nostro codice la flessibilità di adattarsi a diversi tipi di dati con l'aiuto di classi e metodi generici.

2. Manutenzione e riusabilità del codice : a causa di classi e metodi generici, non è necessario riscrivere il codice, in caso di una modifica dei requisiti in una fase successiva che semplifichi la manutenzione e il riutilizzo del codice.

3. Sicurezza del tipo: fornisce la sicurezza del tipo al framework di raccolta definendo il tipo di dati che la raccolta può contenere in anticipo ed eliminando qualsiasi possibilità di errore durante l'esecuzione a causa di ClassCastException.

4. Eliminazione della necessità di eseguire il typecast: poiché i tipi di dati conservati dalle raccolte sono già determinati, non è necessario eseguirne il cast al momento del recupero. Ciò riduce la lunghezza del codice e riduce anche lo sforzo di un programmatore.

Generici nelle abilità Java

Per lavorare con Generics, dovresti avere una buona conoscenza delle basi di Java. Dovresti capire come funzionano la verifica del tipo e la fusione del tipo. Sono necessarie una conoscenza approfondita di altri concetti come il sovraccarico del metodo, la relazione tra classi genitore e figlio, interfacce e relative implementazioni. Comprendere anche la differenza tra tipi di dati primitivi (tipo di dati definiti dal sistema) e oggetti (tipo di dati definiti dall'utente) è cruciale quando si tratta di lavorare con il framework di raccolta.

Perché dovremmo usare Generics in Java?

L'uso di generics rende il nostro codice più gestibile in quanto riduce la necessità di riscrivere il codice specifico del tipo di dati ogni volta che si verifica una modifica dei requisiti. Utilizzando il tipo limitato generici è possibile limitare il tipo di dati e allo stesso tempo fornire flessibilità al codice definendone l'intervallo. È più probabile che il codice non riesca in un secondo momento in quanto fornisce la sicurezza del tipo rendendo il codice meno soggetto a errori.

Scope for Generics in Java

L'ambito di Generics è limitato al tempo di compilazione. Ciò significa che il concetto di generica è applicabile solo in fase di compilazione ma non in fase di esecuzione. Per esempio,

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

Qui tutte e quattro le affermazioni di cui sopra sono la stessa cosa. Consentiranno l'aggiunta di qualsiasi tipo di dati all'oggetto elenco.

Conclusione

Generics semplifica la codifica per un programmatore. Riduce le possibilità di incontrare ClassCastException in fase di esecuzione fornendo un forte controllo del tipo. Elimina completamente la necessità di cast del tipo, il che significa che è necessario scrivere meno codice. Ci offre la possibilità di sviluppare algoritmi generici indipendenti dal tipo di dati con cui stanno lavorando.

Articoli consigliati

Questa è stata una guida a Cos'è Generics in Java ?. Qui abbiamo discusso le competenze, l'ambito, il funzionamento, la comprensione e il vantaggio di Generics in Java. Puoi anche consultare i nostri altri articoli suggeriti per saperne di più -

  1. Cos'è Common Gateway Interface
  2. Come installare Java 8
  3. che cos'è soapUI
  4. Che cos'è JavaScript?
  5. Java booleani

Categoria: