Costruttori generici in Java

1. Panoramica

In precedenza abbiamo discusso le basi di Java Generics. In questo tutorial, daremo un'occhiata ai costruttori generici in Java.

Un costruttore generico è un costruttore che ha almeno un parametro di un tipo generico.

Vedremo che i costruttori generici non devono essere in una classe generica e non tutti i costruttori in una classe generica devono essere generici.

2. Classe non generica

Innanzitutto, abbiamo una semplice classe Entry , che non è una classe generica:

public class Entry { private String data; private int rank; }

In questa classe aggiungeremo due costruttori: un costruttore di base con due parametri e un costruttore generico.

2.1. Costruttore di base

Il primo costruttore di Entry è un semplice costruttore con due parametri:

public Entry(String data, int rank) { this.data = data; this.rank = rank; }

Ora, usiamo questo costruttore di base per creare un oggetto Entry :

@Test public void givenNonGenericConstructor_whenCreateNonGenericEntry_thenOK() { Entry entry = new Entry("sample", 1); assertEquals("sample", entry.getData()); assertEquals(1, entry.getRank()); }

2.2. Costruttore generico

Successivamente, il nostro secondo costruttore è un costruttore generico:

public  Entry(E element) { this.data = element.toString(); this.rank = element.getRank(); }

Anche se l' ingresso di classe non è generica, ha un costruttore generica, in quanto ha un parametro di elemento di tipo E .

Il tipo generico E è limitato e dovrebbe implementare sia interfacce Rankable che Serializable .

Ora, diamo un'occhiata all'interfaccia Rankable , che ha un metodo:

public interface Rankable { public int getRank(); }

E supponiamo di avere una classe Product che implementa l' interfaccia Rankable :

public class Product implements Rankable, Serializable { private String name; private double price; private int sales; public Product(String name, double price) { this.name = name; this.price = price; } @Override public int getRank() { return sales; } }

Possiamo quindi utilizzare il costruttore generico per creare oggetti Entry utilizzando un Prodotto :

@Test public void givenGenericConstructor_whenCreateNonGenericEntry_thenOK() { Product product = new Product("milk", 2.5); product.setSales(30); Entry entry = new Entry(product); assertEquals(product.toString(), entry.getData()); assertEquals(30, entry.getRank()); }

3. Classe generica

Successivamente, daremo uno sguardo a una classe generica chiamata GenericEntry :

public class GenericEntry { private T data; private int rank; }

Aggiungeremo anche gli stessi due tipi di costruttori della sezione precedente in questa classe.

3.1. Costruttore di base

Per prima cosa, scriviamo un semplice costruttore non generico per la nostra classe GenericEntry :

public GenericEntry(int rank) { this.rank = rank; }

Anche se GenericEntry è una classe generica, si tratta di un semplice costruttore che non dispone di un parametro di un tipo generico.

Ora possiamo usare questo costruttore per creare un GenericEntry :

@Test public void givenNonGenericConstructor_whenCreateGenericEntry_thenOK() { GenericEntry entry = new GenericEntry(1); assertNull(entry.getData()); assertEquals(1, entry.getRank()); }

3.2. Costruttore generico

Successivamente, aggiungiamo il secondo costruttore alla nostra classe:

public GenericEntry(T data, int rank) { this.data = data; this.rank = rank; }

Questo è un costruttore generico, in quanto ha un dati parametro del tipo generico T . Nota che non è necessario aggiungere nella dichiarazione del costruttore, poiché è implicitamente lì.

Ora, testiamo il nostro costruttore generico:

@Test public void givenGenericConstructor_whenCreateGenericEntry_thenOK() { GenericEntry entry = new GenericEntry("sample", 1); assertEquals("sample", entry.getData()); assertEquals(1, entry.getRank()); }

4. Costruttore generico con tipo diverso

Nella nostra classe generica, possiamo anche avere un costruttore con un tipo generico diverso dal tipo generico della classe:

public  GenericEntry(E element) { this.data = (T) element; this.rank = element.getRank(); }

Questo costruttore GenericEntry ha un elemento parametro con tipo E , che è diverso dal tipo T. Vediamolo in azione:

@Test public void givenGenericConstructorWithDifferentType_whenCreateGenericEntry_thenOK() { Product product = new Product("milk", 2.5); product.setSales(30); GenericEntry entry = new GenericEntry(product); assertEquals(product, entry.getData()); assertEquals(30, entry.getRank()); }

Nota che:

  • Nel nostro esempio, abbiamo utilizzato Product ( E ) per creare un GenericEntry di tipo Serializable ( T )
  • Possiamo usare questo costruttore solo quando il parametro di tipo E può essere convertito in T

5. Tipi generici multipli

Successivamente, abbiamo la classe generica MapEntry con due tipi generici:

public class MapEntry { private K key; private V value; public MapEntry(K key, V value) { this.key = key; this.value = value; } }

MapEntry ha un costruttore generico con due parametri, ciascuno di un tipo diverso. Usiamolo in un semplice unit test:

@Test public void givenGenericConstructor_whenCreateGenericEntryWithTwoTypes_thenOK() { MapEntry entry = new MapEntry("sample", 1); assertEquals("sample", entry.getKey()); assertEquals(1, entry.getValue().intValue()); }

6. Caratteri jolly

Infine, possiamo usare i caratteri jolly in un costruttore generico:

public GenericEntry(Optional optional) { if (optional.isPresent()) { this.data = (T) optional.get(); this.rank = optional.get().getRank(); } }

Qui, abbiamo utilizzato i caratteri jolly in questo costruttore GenericEntry per associare il tipo opzionale :

@Test public void givenGenericConstructorWithWildCard_whenCreateGenericEntry_thenOK() { Product product = new Product("milk", 2.5); product.setSales(30); Optional optional = Optional.of(product); GenericEntry entry = new GenericEntry(optional); assertEquals(product, entry.getData()); assertEquals(30, entry.getRank()); }

Si noti che dovremmo essere in grado di eseguire il cast del tipo di parametro opzionale (nel nostro caso, Product ) al tipo GenericEntry (nel nostro caso, Serializable ).

7. Conclusione

In questo articolo abbiamo appreso come definire e utilizzare costruttori generici in classi sia generiche che non generiche.

Il codice sorgente completo può essere trovato su GitHub.