Lorsque vous développez en équipe avec un grand nombre de personnes, le traitement que vous souhaitez faire en commun sort. Par exemple, les opérations de chaîne de caractères et les opérations de date sont utilisées dans diverses scènes, mais dans ce cas, je pense que vous utiliserez une bibliothèque pratique ou que vous créerez une classe avec des méthodes utiles.
S'il s'agit d'un processus pour une classe spécifique, il peut être créé dans une certaine mesure facilement, mais lors du développement avec un grand nombre de personnes, il y a une demande d'exécuter un processus commun pour différentes classes.
Quelque chose qui est pratique à utiliser à ce moment-là est "générique" ou "interface fonctionnelle". Même si vous connaissez ces deux choses, il peut être difficile pour certaines personnes de comprendre comment les utiliser réellement.
Donc, cette fois, je voudrais vous présenter comment l'utiliser réellement tout en suivant le processus de création d'une méthode pratique.
En le passant comme un paramètre à la définition du type de données, il est possible de prendre en charge un programme avec une structure similaire pour plusieurs types de données.
Par exemple, jusqu'au JDK1.4, lorsqu'il s'agissait de List, il n'y avait pas de spécification de type et tout pouvait être entré en tant qu'objet.
List list = new ArrayList();
list.add("string");
list.add(Integer.valueOf(100));
Si le type n'est pas limité et que vous ne souhaitez saisir que des caractères, la personne qui crée le programme doit faire attention. Après la sortie des génériques, il est devenu possible de spécifier le type, et il est devenu possible d'écrire comme suit.
List<String> list = new ArrayList<>();
list.add("string1");
list.add("string2");
En regardant les spécifications de l'API Java, elles sont répertoriées comme ** List \ <E > ** et ** ArrayList \ <E > **, respectivement. Cette partie ** E ** est appelée générique, et n'importe quel type peut être défini pour les programmes avec la même structure.
Une simple représentation d'une interface fonctionnelle est une interface qui vous permet d'attribuer des «références de méthode» et des «expressions lambda» introduites dans Java 8.
Par exemple, getter et setter, qui sont souvent créés en Java, sont représentés par une interface fonctionnelle comme suit.
Méthode | interface | Méthode |
---|---|---|
getter | Supplier<T> | T get() |
setter | Consumer<T> | accept(T t) |
Le fournisseur est une interface qui n'a aucun argument et renvoie n'importe quel type. Des compagnons similaires incluent IntSupplier, BooleanSupplier, etc., qui renvoient des primitives plutôt que des arbitraires.
Consumer est une interface qui prend des arguments et n'a pas de valeur de retour. Il existe également IntConsumer, DoubleConsumer, etc. qui transmettent des primitives à des pairs similaires plutôt que de manière arbitraire.
Pour ceux qui n'ont jamais utilisé de génériques ou d'interfaces fonctionnelles, l'explication ci-dessus peut être difficile. Par conséquent, je vais vous expliquer comment l'utiliser facilement tout en créant une méthode pratique.
Disons que vous avez créé une classe d'étudiants. On suppose que la classe d'élèves a un numéro d'inscription scolaire, un nom d'élève et un âge, et le setter et le getter sont définis.
public class Student {
/**Numéro d'étudiant*/
private String code;
/**Nom d'étudiant*/
private String name;
/**âge*/
private int age;
//... setter,abréviation getter...
}
Afin de préparer les données pour le test unitaire, nous allons créer trois étudiants en tant que données de test et les mettre dans une liste.
final Student student1 = new Student();
student1.setCode("S01001");
student1.setName("Yamada Taro");
student1.setAge(20);
final Student student2 = new Student();
student1.setCode("S02001");
student1.setName("Jiro Yamada");
student1.setAge(19);
final Student student3 = new Student();
student1.setCode("S03001");
student1.setName("Saburo Yamada");
student1.setAge(18);
final List<Student> students = new ArrayList<>();
students.add(student1);
students.add(student2);
students.add(student3);
Si c'est pour 3 personnes, je ne peux pas encore le voir, mais si ce nombre passe à 10 ou 20, ce sera un gros problème.
Tout d'abord, j'ai créé une méthode qui simplifie la création de List.
public <T> List<T> createInstanceList(Supplier<T> supplier, int size) {
return IntStream.range(0, size)
.mapToObj(i -> supplier.get())
.collect(toList());
}
J'expliquerai ce programme petit à petit.
public <T> List<T> createInstanceList(Supplier<T> supplier, int size)
Tout d'abord, concernant la déclaration de la méthode, \ <T > et les génériques sont définis. Puisqu'il utilise des génériques, il est devenu une méthode qui peut être utilisée pour n'importe quelle classe.
Puisque le type de retour est ** List \ <T > **, une liste de n'importe quel type sera retournée.
Puisque le premier argument est déclaré ** Fournisseur \ <T > **, vous pouvez recevoir une interface juste pour renvoyer un type arbitraire comme mentionné dans l'introduction de Generics.
Le premier argument de chargement est ** int **, ce qui vous permet de spécifier la taille de la liste.
IntStream.range(0, size)
Vient ensuite la valeur à renvoyer. Tout d'abord, le flux est créé avec ** IntStream.range (0, size) **. Répétez de 0 à la taille -1 car vous utilisez la plage. Si la taille est 3, les nombres 0, 1, 2 sont répétés.
.mapToObj(i -> supplier.get())
IntStream a passé 0, 1, 2 plus tôt, mais je l'ignore et j'utilise ** supplier.get () **. Cela appelle la méthode get de l'interface de fonction passée en argument et renvoie le résultat.
.collect(toList());
Enfin, le nombre de fois que le nombre est répété (3 fois dans l'exemple) La valeur reçue de l'interface fonctionnelle est emballée dans une liste et renvoyée.
Comme certaines parties sont difficiles à expliquer, modifions le premier code pour voir comment cette méthode change réellement.
final List<Student> students = createInstanceList(Student::new, 3);
students.get(0).setCode("S01001");
students.get(0).setName("Yamada Taro");
students.get(0).setAge(20);
students.get(1).setCode("S02001");
students.get(1).setName("Jiro Yamada");
students.get(1).setAge(19);
students.get(2).setCode("S03001");
students.get(2).setName("Saburo Yamada");
students.get(2).setAge(18);
L'instanciation de l'étudiant et définie sur Liste ont disparu.
final List<Student> students = createInstanceList(Student::new, 3);
Cette première ligne crée une liste et une instance interne en même temps. ** Student :: new ** est dit être une ** référence de constructeur ** et renvoie une ** interface fonctionnelle ** qui renvoie le résultat de new Student ().
La référence du constructeur sera traitée comme un fournisseur
J'ai créé la méthode plus tôt et le code est plus court, mais la définition de la valeur est toujours compliquée. Par conséquent, j'écrirai le code suivant.
public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values) {
for (int i = 0; i < obj.size(); i++) {
biConsumer.accept(obj.get(i), values[i]);
}
}
J'expliquerai ce programme petit à petit.
public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values)
Tout d'abord, concernant la déclaration de la méthode, ** \ <T, U > ** et les génériques sont définis. Contrairement à la première méthode, elle gère deux types arbitraires.
Étant donné que le type de retour est ** void **, aucune valeur n'est renvoyée.
Puisque le premier argument est ** List
Le deuxième argument est ** BiConsumer <T, U> biConsumer **. Cela sera expliqué plus tard.
Le troisième argument est ** U ... values **, et des arguments variables peuvent être passés. Avec cet argument, vous pouvez définir la valeur que vous souhaitez définir pour les objets de la variable List.
Modifions le code pour voir comment il change lorsque nous utilisons réellement cette méthode.
final List<Student> students = createInstanceList(Student::new, 3);
setValues(students, Student::setCode, "S01001", "S02001", "S03001");
setValues(students, Student::setName, "Yamada Taro", "Jiro Yamada", "Saburo Yamada");
setValues(students, Student::setAge, 20, 19, 18);
Le code est beaucoup plus court et les perspectives sont meilleures.
Je souhaite définir une valeur pour le premier argument ** J'ai une liste d'étudiants **.
Nous transmettons une ** référence de méthode de définition ** comme deuxième argument. Cette référence de méthode est passée en tant que BiConsumer \ <T, U > pour la méthode que nous venons de créer et est appelée par biConsumer.accept (...).
Étant donné que le troisième argument et les suivants sont des ** arguments variables **, répertoriez les valeurs que vous souhaitez définir.
J'ai une question ici. La méthode setter doit normalement utiliser ** Consumer \ <T > ** car elle a un argument et aucune valeur de retour. Maintenant, expliquons brièvement pourquoi nous utilisons BiConsumer, une interface fonctionnelle qui prend deux arguments.
Par exemple, si vous utilisez Conumer \ <T > comme argument, vous pouvez créer le programme suivant.
public <T> void setValue(Consumer<T> consumer, T value) {
consumer.accept(value);
}
Voici comment l'utiliser.
Student student = new Student();
setValue(student::setCode, "S01001");
Les différences par rapport à l'utilisation de BiConsumer sont les suivantes.
interface | Référence de la méthode |
---|---|
BiConsumer<T, U> | Student::setCode |
Consumer<T> | student::setCode |
Lors de l'utilisation de BiConsumer, la référence de méthode de la classe est utilisée, et lors de l'utilisation de Consumer, ** la référence de méthode de l'instance ** est utilisée.
Puisque nous voulons définir la valeur de l'instance dans List cette fois, cela n'a pas de sens de transmettre une référence de méthode pour chaque instance de List. Par conséquent, je passe ** une référence de méthode de la classe ** et j'appelle setter pour chaque instance de la liste.
Les deux méthodes suivantes ont été créées cette fois.
public <T> List<T> createInstanceList(Supplier<T> supplier, int size) {
return IntStream.range(0, size)
.mapToObj(i -> supplier.get())
.collect(toList());
}
public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values) {
for (int i = 0; i < obj.size(); i++) {
biConsumer.accept(obj.get(i), values[i]);
}
}
Je travaille généralement sur le développement avec de nombreux étudiants. Puisqu'il existe de grandes différences de compétences, il est essentiel de pouvoir écrire autant que possible des programmes similaires et de créer des méthodes pratiques pour améliorer l'efficacité du développement.
Vous pouvez créer rapidement des méthodes simples et pratiques, mais plus vous essayez de créer des méthodes aussi génériques que possible, plus vous serez pratique si vous connaissez les génériques et les interfaces fonctionnelles présentées ici.
Recommended Posts