Le type de retour covariant en Java

1. Vue d'ensemble

Dans ce didacticiel, nous allons examiner de plus près le type de retour covariant en Java. Avant d'examiner la covariance du point de vue du type de retour, voyons ce que cela signifie.

2. Covariance

La covariance peut être considérée comme un contrat pour la manière dont un sous-type est accepté lorsque seul le supertype est défini.

Considérons quelques exemples de base de covariance:

List integerList = new ArrayList(); List doubleList = new ArrayList();

La covariance signifie donc que nous pouvons accéder à des éléments spécifiques définis via leur supertype . Cependant, nous ne sommes pas autorisés à placer des éléments dans un système covariant , car le compilateur ne parviendrait pas à déterminer le type réel de la structure générique.

3. Le type de retour covariant

Le type de retour covariant est - lorsque nous surchargons une méthode - ce qui permet au type de retour d'être le sous-type du type de la méthode surchargée .

Pour mettre cela en pratique, prenons une classe Producer simple avec une méthode Producer () . Par défaut, il renvoie une chaîne en tant qu'objet pour offrir de la flexibilité aux classes enfants:

public class Producer { public Object produce(String input) { Object result = input.toLowerCase(); return result; } }

En raison de l' Object comme type de retour, nous pouvons avoir un type de retour plus concret dans la classe enfant. Ce sera le type de retour covariant et produira des nombres à partir de séquences de caractères:

public class IntegerProducer extends Producer { @Override public Integer produce(String input) { return Integer.parseInt(input); } }

4. L'utilisation de la structure

L'idée principale derrière les types de retour covariants est de prendre en charge la substitution de Liskov.

Par exemple, considérons le scénario producteur suivant:

@Test public void whenInputIsArbitrary_thenProducerProducesString() { String arbitraryInput = "just a random text"; Producer producer = new Producer(); Object objectOutput = producer.produce(arbitraryInput); assertEquals(arbitraryInput, objectOutput); assertEquals(String.class, objectOutput.getClass()); }

Après avoir changé pour IntegerProducer , la logique métier qui produit réellement le résultat peut rester la même:

@Test public void whenInputIsSupported_thenProducerCreatesInteger() { String integerAsString = "42"; Producer producer = new IntegerProducer(); Object result = producer.produce(integerAsString); assertEquals(Integer.class, result.getClass()); assertEquals(Integer.parseInt(integerAsString), result); }

Cependant, nous référençons toujours le résultat via un objet. Chaque fois que nous commençons à utiliser une référence explicite à IntegerProducer, nous pouvons récupérer le résultat en tant qu'Integer sans downcasting:

@Test public void whenInputIsSupported_thenIntegerProducerCreatesIntegerWithoutCasting() { String integerAsString = "42"; IntegerProducer producer = new IntegerProducer(); Integer result = producer.produce(integerAsString); assertEquals(Integer.parseInt(integerAsString), result); }

Un scénario bien connu est la méthode de clonage Object # , qui retourne un Object par défaut. Chaque fois que nous surchargons la méthode clone () , la facilité des types de retour covariants nous permet d'avoir un objet de retour plus concret que l' Object lui-même.

5. Conclusion

Dans cet article, nous avons vu ce que sont la covariance et les types de retour covariants et comment ils se comportent en Java.

Comme toujours, le code est disponible sur sur GitHub.