Constructeurs génériques en Java

1. Vue d'ensemble

Nous avons précédemment discuté des bases de Java Generics. Dans ce didacticiel, nous examinerons les constructeurs génériques en Java.

Un constructeur générique est un constructeur qui a au moins un paramètre d'un type générique.

Nous verrons que les constructeurs génériques n'ont pas besoin d'être dans une classe générique, et que tous les constructeurs d'une classe générique ne doivent pas nécessairement être génériques.

2. Classe non générique

Tout d'abord, nous avons une simple classe Entry , qui n'est pas une classe générique:

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

Dans cette classe, nous ajouterons deux constructeurs: un constructeur de base avec deux paramètres et un constructeur générique.

2.1. Constructeur de base

Le premier constructeur Entry est un constructeur simple avec deux paramètres:

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

Maintenant, utilisons ce constructeur de base pour créer un objet Entry :

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

2.2. Constructeur générique

Ensuite, notre deuxième constructeur est un constructeur générique:

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

Bien que l' entrée classe n'est pas générique, il a un constructeur générique, car il a un paramètre élément de type E .

Le type générique E est borné et doit implémenter à la fois des interfaces classables et sérialisables .

Maintenant, jetons un coup d'œil à l' interface Rankable , qui a une méthode:

public interface Rankable { public int getRank(); }

Et, supposons que nous ayons une classe Product qui implémente l' interface 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; } }

Nous pouvons ensuite utiliser le constructeur générique pour créer des objets Entry à l' aide d'un Product :

@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 générique

Ensuite, nous allons jeter un œil à une classe générique appelée GenericEntry :

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

Nous ajouterons également les deux mêmes types de constructeurs que la section précédente de cette classe.

3.1. Constructeur de base

Tout d'abord, écrivons un constructeur simple et non générique pour notre classe GenericEntry :

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

Même si GenericEntry est une classe générique, il s'agit d'un constructeur simple qui n'a pas de paramètre de type générique.

Maintenant, nous pouvons utiliser ce constructeur pour créer un GenericEntry :

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

3.2. Constructeur générique

Ensuite, ajoutons le deuxième constructeur à notre classe:

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

Ceci est un constructeur générique, car il a un données paramètre de type générique T . Notez que nous n'avons pas besoin d'ajouter dans la déclaration du constructeur, car il est implicitement là.

Maintenant, testons notre constructeur générique:

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

4. Constructeur générique avec un type différent

Dans notre classe générique, nous pouvons également avoir un constructeur avec un type générique différent du type générique de la classe:

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

Ce constructeur GenericEntry a un élément de paramètre de type E , qui est différent du type T. Voyons cela en action:

@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()); }

Notez que:

  • Dans notre exemple, nous avons utilisé Product ( E ) pour créer une GenericEntry de type Serializable ( T )
  • Nous ne pouvons utiliser ce constructeur que lorsque le paramètre de type E peut être converti en T

5. Plusieurs types génériques

Ensuite, nous avons la classe générique MapEntry avec deux types génériques:

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

MapEntry a un constructeur générique avec deux paramètres, chacun d'un type différent. Utilisons-le dans un simple test unitaire:

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

6. Caractères génériques

Enfin, nous pouvons utiliser des jokers dans un constructeur générique:

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

Ici, nous avons utilisé des caractères génériques dans ce constructeur GenericEntry pour lier le type facultatif :

@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()); }

Notez que nous devrions pouvoir convertir le type de paramètre facultatif (dans notre cas, Product ) en type GenericEntry (dans notre cas, Serializable ).

7. Conclusion

Dans cet article, nous avons appris à définir et à utiliser des constructeurs génériques dans des classes génériques et non génériques.

Le code source complet est disponible à l'adresse over sur GitHub.