Bei der Entwicklung in Teams mit einer großen Anzahl von Personen kommt die Verarbeitung heraus, die Sie gemeinsam ausführen möchten. Zum Beispiel werden Zeichenfolgenoperationen und Datumsoperationen in verschiedenen Szenen verwendet, aber in diesem Fall denke ich, dass Sie eine praktische Bibliothek verwenden oder eine Klasse mit nützlichen Methoden erstellen werden.
Wenn es sich um einen Prozess für eine bestimmte Klasse handelt, kann er bis zu einem gewissen Grad leicht erstellt werden. Bei der Entwicklung mit einer großen Anzahl von Personen besteht jedoch die Anforderung, einen gemeinsamen Prozess für verschiedene Klassen durchzuführen.
Etwas, das zu dieser Zeit bequem zu verwenden ist, ist "Generika" oder "funktionale Schnittstelle". Selbst wenn Sie über diese beiden Dinge Bescheid wissen, kann es für manche Menschen schwierig sein, zu verstehen, wie sie tatsächlich verwendet werden.
Dieses Mal möchte ich Ihnen vorstellen, wie Sie es tatsächlich verwenden, während Sie den Prozess der tatsächlichen Erstellung einer praktischen Methode verfolgen.
Indem Sie es wie einen Parameter an die Definition des Datentyps übergeben, können Sie ein Programm mit einer ähnlichen Struktur für mehrere Datentypen unterstützen.
Beispielsweise gab es bis JDK1.4 beim Umgang mit List keine Typspezifikation und alles konnte als Objekt eingegeben werden.
List list = new ArrayList();
list.add("string");
list.add(Integer.valueOf(100));
Wenn der Typ nicht eingeschränkt ist und Sie nur Zeichen eingeben möchten, muss die Person, die das Programm erstellt, vorsichtig sein. Nachdem die Generika herauskamen, wurde es möglich, den Typ anzugeben, und es wurde möglich, wie folgt zu schreiben.
List<String> list = new ArrayList<>();
list.add("string1");
list.add("string2");
In den Java-API-Spezifikationen werden sie als ** List \ <E > ** bzw. ** ArrayList \ <E > ** aufgeführt. Dieser ** E ** -Teil wird als Generics bezeichnet, und für Programme mit derselben Struktur kann ein beliebiger Typ festgelegt werden.
Eine einfache Darstellung einer funktionalen Schnittstelle ist eine Schnittstelle, mit der Sie in Java 8 eingeführte "Methodenreferenzen" und "Lambda-Ausdrücke" zuweisen können.
Zum Beispiel werden Getter und Setter, die häufig in Java erstellt werden, durch eine funktionale Schnittstelle wie folgt dargestellt.
Methode | Schnittstelle | Methode |
---|---|---|
getter | Supplier<T> | T get() |
setter | Consumer<T> | accept(T t) |
Supplier ist eine Schnittstelle, die keine Argumente hat und einen beliebigen Typ zurückgibt. Ähnliche Begleiter sind IntSupplier, BooleanSupplier usw., die eher Grundelemente als willkürliche zurückgeben.
Consumer ist eine Schnittstelle, die Argumente akzeptiert und keinen Rückgabewert hat. Es gibt auch IntConsumer, DoubleConsumer usw., die Grundelemente nicht willkürlich, sondern an ähnliche Peers übergeben.
Für diejenigen, die noch nie Generika oder funktionale Schnittstellen verwendet haben, kann die obige Erklärung schwierig sein. Daher werde ich erklären, wie man es einfach benutzt, während man tatsächlich eine bequeme Methode erstellt.
Angenommen, Sie haben eine Schülerklasse erstellt. Es wird angenommen, dass die Schülerklasse eine Schulregistrierungsnummer, einen Schülernamen und ein Alter hat und Setter und Getter definiert sind.
public class Student {
/**Schülernummer*/
private String code;
/**Name des Studenten*/
private String name;
/**Alter*/
private int age;
//... setter,Getter Abkürzung...
}
Um die Daten für den Komponententest vorzubereiten, erstellen wir drei Schüler als Testdaten und fügen sie in eine Liste ein.
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);
Wenn es für 3 Personen ist, kann ich es noch nicht sehen, aber wenn diese Zahl auf 10 oder 20 steigt, wird es eine große Sache sein.
Zuerst habe ich eine Methode erstellt, die das Erstellen von Listen vereinfacht.
public <T> List<T> createInstanceList(Supplier<T> supplier, int size) {
return IntStream.range(0, size)
.mapToObj(i -> supplier.get())
.collect(toList());
}
Ich werde dieses Programm nach und nach erklären.
public <T> List<T> createInstanceList(Supplier<T> supplier, int size)
In Bezug auf die Deklaration der Methode werden zunächst \ <T > und die Generika definiert. Da es Generika verwendet, ist es zu einer Methode geworden, die für jede Klasse verwendet werden kann.
Da der Rückgabetyp ** List \ <T > ** ist, wird eine Liste eines beliebigen Typs zurückgegeben.
Da das erste Argument als ** Lieferant \ <T > ** deklariert ist, können Sie eine Schnittstelle erhalten, um einen beliebigen Typ zurückzugeben, wie in der Einführung von Generics erwähnt.
Das erste Ladeargument ist ** int **, mit dem Sie die Größe der Liste angeben können.
IntStream.range(0, size)
Als nächstes wird der Wert zurückgegeben. Zuerst wird der Stream mit ** IntStream.range (0, Größe) ** erstellt. Wiederholen Sie diesen Vorgang von 0 bis Größe -1, da Sie den Bereich verwenden. Wenn die Größe 3 ist, werden die Zahlen 0, 1, 2 wiederholt.
.mapToObj(i -> supplier.get())
IntStream hat 0, 1, 2 früher bestanden, aber ich ignoriere es und verwende ** vendor.get () **. Dies ruft die get-Methode der als Argument übergebenen Funktionsschnittstelle auf und gibt das Ergebnis zurück.
.collect(toList());
Schließlich wird die Anzahl der Wiederholungen wiederholt (im Beispiel dreimal). Der von der Funktionsschnittstelle empfangene Wert wird in eine Liste gepackt und zurückgegeben.
Da es einige Teile gibt, die schwer zu erklären sind, ändern wir den ersten Code, um zu sehen, wie sich diese Methode tatsächlich ändert.
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);
Die Schülerinstanziierung und die Einstellung auf Liste sind weg.
final List<Student> students = createInstanceList(Student::new, 3);
In dieser ersten Zeile werden gleichzeitig eine Liste und eine interne Instanz erstellt. ** Student :: new ** wird als ** Konstruktorreferenz ** bezeichnet und gibt eine ** Funktionsschnittstelle ** zurück, die das Ergebnis von new Student () zurückgibt.
Die Konstruktorreferenz wird als Lieferant
Ich habe die Methode früher erstellt und der Code ist kürzer, aber das Festlegen des Werts ist immer noch kompliziert. Daher werde ich den folgenden Code schreiben.
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]);
}
}
Ich werde dieses Programm nach und nach erklären.
public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values)
In Bezug auf die Deklaration der Methode werden zunächst ** \ <T, U > ** und die Generika definiert. Im Gegensatz zur ersten Methode werden zwei beliebige Typen behandelt.
Da der Rückgabetyp ** void ** ist, wird kein Wert zurückgegeben.
Da das erste Argument ** List
Das zweite Argument ist ** BiConsumer <T, U> biConsumer **. Dies wird später erklärt.
Das dritte Argument ist ** U ... Werte **, und variable Argumente können übergeben werden. Mit diesem Argument können Sie den Wert festlegen, den Sie für die Objekte in der List-Variablen festlegen möchten.
Lassen Sie uns den Code ändern, um zu sehen, wie er sich ändert, wenn wir diese Methode tatsächlich verwenden.
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);
Der Code ist viel kürzer und die Aussichten sind besser.
Ich möchte einen Wert für das erste Argument festlegen ** Ich habe eine Liste von Studenten **.
Als zweites Argument übergeben wir eine ** Setter-Methodenreferenz **. Diese Methodenreferenz wird als BiConsumer \ <T, U > für die gerade erstellte Methode übergeben und in biConsumer.accept (...) aufgerufen.
Da das dritte und die nachfolgenden Argumente ** variable Argumente ** sind, listen Sie die Werte auf, die Sie festlegen möchten.
Ich habe hier eine Frage. Die Setter-Methode sollte normalerweise ** Consumer \ <T > ** verwenden, da sie ein Argument und keinen Rückgabewert enthält. Lassen Sie uns nun kurz erklären, warum wir BiConsumer verwenden, eine funktionale Schnittstelle, die zwei Argumente akzeptiert.
Wenn Sie beispielsweise Conumer \ <T > als Argument verwenden, können Sie das folgende Programm erstellen.
public <T> void setValue(Consumer<T> consumer, T value) {
consumer.accept(value);
}
Hier erfahren Sie, wie Sie es verwenden.
Student student = new Student();
setValue(student::setCode, "S01001");
Die Unterschiede zur Verwendung von BiConsumer sind wie folgt.
Schnittstelle | Methodenreferenz |
---|---|
BiConsumer<T, U> | Student::setCode |
Consumer<T> | student::setCode |
Bei Verwendung von BiConsumer wird die Methodenreferenz der Klasse verwendet, und bei Verwendung von Consumer wird ** die Methodenreferenz der Instanz ** verwendet.
Da wir diesmal den Wert für die Instanz in List festlegen möchten, ist es nicht sinnvoll, für jede Instanz in List eine Methodenreferenz zu übergeben. Daher übergebe ich ** Klassenmethodenreferenz ** und Aufrufsetter für jede Instanz in List.
Die folgenden zwei Methoden wurden dieses Mal erstellt.
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]);
}
}
Normalerweise arbeite ich mit vielen Studenten an der Entwicklung. Da es große Unterschiede in den Fähigkeiten gibt, ist es wichtig, so viele ähnliche Programme wie möglich schreiben zu können und bequeme Methoden zur Verbesserung der Entwicklungseffizienz zu entwickeln.
Sie können schnell einfache und bequeme Methoden erstellen. Je mehr Sie jedoch versuchen, so allgemein wie möglich zu erstellen, desto bequemer werden Sie, wenn Sie die hier vorgestellten generischen und funktionalen Schnittstellen kennen.