Résumé des génériques Java

introduction

J'ai de nouveau fait des recherches sur les génériques ces derniers jours, alors après avoir donné un bref aperçu des génériques, j'ai décidé de résumer les deux contenus suivants sous forme de mémo.

Présentation des génériques

Quoi qu'il en soit, je voudrais réorganiser les avantages de l'utilisation des génériques. Il ** vous avertira au moment de la compilation si vous essayez d'insérer un objet du mauvais type ** (cité dans Effective Java 3rd Edition Chapter 5 Generics). En conséquence, un traitement similaire peut être réalisé avec différents types dans la plage dans laquelle la sécurité de type peut être garantie. Maintenant, je voudrais montrer un exemple concret dans le code. Ici, je voudrais comparer des séquences et des génériques et reconnaître la bonté d'écrire dans les génériques. Écrivez la définition de la liste, l'ajout à la liste et l'extraction de la liste sans utiliser de génériques comme suit.

ArrayList list = new ArrayList(); 
list.add("hoge"); 
String s = (String) list.get(0);

Puisqu'il est extrait en tant que type Object, il est nécessaire de le convertir explicitement, et s'il y a une erreur (telle que l'affectation avec int et l'extraction avec String), vous ne remarquerez pas l'erreur à moins que vous ne la déplaciez. ← C'est très douloureux. Écrivez la définition de la liste à l'aide des génériques, l'ajout à la liste et l'extraction comme suit.

ArrayList<String> list = new ArrayList<String>(); 
list.add("hoge"); 
String s = list.get(0);

J'obtiens une erreur lors de la compilation. (Vous pouvez remarquer l'erreur avant de l'exécuter ← C'est très agréable)

À propos de la portée des génériques

Generics a deux portées. Portée de la méthode et portée de l'instance. Un aperçu est donné pour chacun.

Portée de la méthode

Tout d'abord, la syntaxe est indiquée ci-dessous.

SetInt.java


public class SetInt{
  public <E> Set<E> union(Set<E> s1,Set<E> s2){
    // (Exemple)Traitement pour ajouter s1 et s2
 }
}

La première chose que vous remarquerez est le après le modificateur d'accès public. Il s'agit d'une déclaration de variable de type. Cette déclaration nous permet de définir une association selon laquelle l'argument et la valeur de retour sont du même type dans le cadre de la méthode. La déclaration générique dans la portée de la méthode peut être utilisée non seulement pour les méthodes d'instance mais aussi pour les méthodes statiques et les constructeurs. Lors de l'appel, il existe des cas où il est appelé tel quel et des cas où il est appelé en spécifiant explicitement le type.

Lors de l'appel tel qu'il est
Set<Integer> result = SetInt.union(Set<Integer>,Set<Integer>);
Lors de l'appel en spécifiant explicitement le type
Set<Integer> result = SetInt.<Integer>union(Set<Integer>,Set<Integer>);

De plus, le type peut être spécifié explicitement et le traitement des exceptions peut être appelé comme suit.

Hoge.java


public class Hoge{
	public <E extends Exception> void example() throws E {};
	public  void test() {
		try {
			this.<IOException>example();
		} catch (IOException e) {
			//Gestion des exceptions lors de la capture d'exception IOException
		}
		try {
			this.<SQLException>example();
		} catch (SQLException e) {
			//Gestion des exceptions lors de la capture d'exception SQLException
		}
	}	
}

Portée de l'instance

Elle est plus souvent vue que la portée de la méthode, et j'estime qu'elle est généralement traitée dans les livres d'introduction. Un exemple est présenté ci-dessous.

Stack.java


public class Stack<E> {
  private E elements;
  public void push(E e) {
	//Processus pour pousser vers les éléments
  }
  public E pop() {
	//Traitement pour extraire E des éléments
  }
}

Vous pouvez définir des associations dans lesquelles les arguments, les valeurs de retour et les types de champs d'instance de plusieurs méthodes d'instance déclarées (deux ci-dessus) sont identiques.

Histoire autour des variables de type

On suppose que vous avez connaissance de «covariant, antivariant, immuable» et du «principe de remplacement de Riskov». Cet article est facile à comprendre sur le premier (https://www.thekingsmuseum.info/entry/2016/02/07/235454) Dans le mémo suivant, à titre d'exemple, il existe des classes A, B et C, respectivement, et elles sont vérifiées comme ayant une relation d'héritage de C étend B et B étend A, respectivement.

Covariant

Il peut être défini comme suit.

List<? extends B> extendsBList = new ArrayList<C>();

L'extensionAList ci-dessus a les propriétés suivantes.

  1. Les variables de type B et de type C peuvent être extraites de extendBList.
  2. Seul le type nul peut être stocké dans extendBList.

Mis à part la propriété 1, la propriété 2 reste discutable. Le processus par lequel la propriété 2 est dérivée est résumé ci-dessous.

Pourquoi ne puis-je pas stocker autre chose que du type nul dans extendBList?

Par exemple, il peut être stocké dans extendBList et extendBList.add (new B ()); tient. Mais encore une fois, veuillez vous référer au code d'initialisation de extendBList. Initialisé avec le type ArrayList . Si B pouvait être ajouté (), le type B serait ajouté () au type ArrayList , provoquant une contradiction. Par conséquent, dans List <? Extends B>, add () peut être ajouté à List et List qui peuvent être assignés à List <? Extends B> afin que la sécurité du type ne soit pas rompue. Une contrainte est placée pour que seuls de nouveaux éléments puissent être ajoutés (). Puisqu'elle n'a qu'un type nul, la propriété 2 tient.

Rébellion

Il peut être défini comme suit.

List<? super B> superBList = new ArrayList<A>();

Le superBList ci-dessus a les propriétés suivantes.

  1. superBList peut stocker des variables de type B et de type A.
  2. Le type extrait de superBList est le type d'objet.

La question reste dans la propriété 2. Cependant, le fait que la propriété 1 soit vérifiée signifie que l'objet récupéré par get () peut être d'un type d'objet supérieur au type B. A partir de là, lorsque <? Super B> est défini, l'objet obtenu par get () ne peut être reçu que par le type Object situé en haut de tous les types. Au lieu d'accepter le stockage, il devient impossible de spécifier le type à renvoyer.

À propos des principes PECS

Le point 28 de la 2e édition effective de Java décrit les principes PECS. Cité ci-dessous.

Dans la fonction, si le rôle de l'argument de type générique est "Producer", utilisez extend, et s'il est "Consumer", utilisez super. Un producteur est un argument qui crée (fournit) une valeur dans une fonction. D'un autre côté, un consommateur est un argument qui consomme (utilise) une certaine valeur dans une fonction. Ce principe est également appelé "PECS" pour abréviation de Producer-Extends et Consumer-Super.

Sur la base de la discussion sur le co-changement et l'anti-changement, nous pouvons comprendre pourquoi nous le faisons. Prenons l'exemple de la classe Hoge.

  • Pour générer (fournir) une valeur du côté de la fonction, pour obtenir la valeur du côté de l'utilisateur de la fonction, c'est-à-dire pour récupérer la valeur, utilisez <? Extends Hoge>.
  • Consommer (utiliser) une valeur du côté de la fonction signifie définir la valeur du côté de l'utilisateur de la fonction, c'est-à-dire <? Super Hoge> lors du stockage de la valeur.

Dans une méthode, l'utilisation de extend comme argument de la méthode et super comme valeur de retour de la méthode peut prendre une implémentation plus large de la méthode.

à la fin

J'aimerais revoir le contenu de l'article de temps en temps. De plus, quand je peux écrire des génériques, je veux être capable de concevoir en utilisant des génériques.

référence

(https://www.amazon.co.jp/exec/obidos/ASIN/B078H61SCH/xray2000-22/)

Recommended Posts

Résumé des génériques Java
[Java] Génériques
Résumé des connaissances Java
[Java] Exemple de génériques
Génériques Java (Notes)
Résumé relatif à Java
Résumé du document Java 8
Résumé du document Java 11
Résumé des nouvelles fonctionnalités de Java 12
[Résumé] Par exemple, préparation de l'environnement Java
3ème résumé efficace de Java
Résumé des nouvelles fonctionnalités de Java 13
Génériques Java fréquemment utilisés
Java statique [Résumé personnel]
Résumé des threads sûrs ~ Java ~
Résumé de la spécialisation des primitives Java
Résumé du lien de développement Java
Résumé personnel sur Java
Résumé des nouvelles fonctionnalités de Java 10
résumé des expressions régulières java
Résumé des nouvelles fonctionnalités de Java 14
Résumé du support Java 2018
Résumé du modèle de conception Java
Résumé du mot réservé Java
Résumé approximatif du flux Java8
Qu'est-ce que l'assertion Java? Résumé.
[Java11] Résumé du flux -Avantages du flux-
Révision et résumé de Progate Java (débutant)
[Java] Classe générique et méthode générique
[Java] Résumé des expressions régulières
[Java] Résumé des opérateurs (opérateur)
Flux Java8, résumé de l'expression lambda
Résumé orienté objet par les débutants (Java)
Résumé
Java
Résumé des bases du langage Java
Astuces Java - Résumé de l'exécution de Spring
Résumé de la classe Java Math
java Generics T et? Différence
Java
[Java11] Résumé de l'utilisation du flux -Basics-
[Java] Résumé de la syntaxe de contrôle
Résumé du traitement des erreurs Java
[Java] Résumé des modèles de conception
[Java] Résumé des opérations mathématiques
Considération sur le cadre de persistance Java 2017 (résumé) -1
[Pour les débutants] Résumé du constructeur java
Date de sortie de Java et résumé EOL
Résumé du package [Java Silver Study]
Efficacité de Java 3rd Edition Chapitre 5 Génériques
Résumé de l'algorithme AtCoder 400 points (édition Java)
[Java] Essayez de mettre en œuvre à l'aide de génériques
Java Generics (définit les classes et les méthodes)
Récapitulatif du problème Java "Pass by Reference"
Génériques Kotlin pour les développeurs Java
Java EE 8 (Jakarta EE 8) Résumé des nouvelles fonctionnalités
Résumé de la programmation orientée objet utilisant Java
Apprendre Java (0)
Étudier Java ―― 3
[Java] tableau