Java und primäre Funktionen - jenseits funktionaler Schnittstellen -

Überblick

Java 8 bietet erweiterte Funktionen für die funktionale Programmierung.

Fassen Sie zusammen, welche Funktionen funktionale Schnittstellen und Methodenreferenzen sind.

Goal Sie können diesen Quellcode lesen und verwenden.

public static void main(String[] args) {
    List<String> strings = Arrays.asList("bravo", "", "charlie", "alpha");
    strings.stream()
            .filter(s -> !s.isEmpty())
            .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
            .forEach(System.out::println);
             //  Bravo
             //  Charlie
             //  Alpha
}

Funktionsschnittstelle

Was ist eine funktionale Schnittstelle und wofür ist sie gedacht? Um es zu verstehen, ist es notwendig, das Konzept der ** Primärfunktion ** zu verstehen.

Erstklassige Funktion

Erstklassige Funktionen (Englisch: erstklassige Funktion) [1] ist die Natur von Programmiersprachen, die Funktionen als erstklassige Objekte oder solche Funktionen behandeln können Das ist.

Erstklassiges Objekt?

Erstklassiges Objekt

Erstklassige Objekte sind unbegrenzte Grundoperationen in einer Programmiersprache, wie z. B. Erstellung, Zuweisung, Arithmetik und Übergabe (als Argumente / Rückgabewerte). Es ist ein Objekt, das verwendet werden kann.

Das war's

Was ist mit Java?

Java-Funktionen (Methoden) können nicht Variablen zugewiesen oder für Rückgabewerte / Argumente verwendet werden. Java ist eine Sprache, die die Eigenschaften erstklassiger Funktionen nicht erfüllt.

Was macht Sie mit einer erstklassigen Funktion glücklich?

Erstklassige Funktionen sind für funktionale Sprachen unverzichtbar und werden täglich in Form von Funktionen höherer Ordnung verwendet.

** Das Verhalten kann durch Funktionen von außen manipuliert werden. ** **. z.B. Sortierregeln

List<String> strings = Arrays.asList("bravo", "charlie", "alpha");

Collections.sort(strings);
System.out.println(strings); // -> [alpha, bravo, charlie]

Collections.sort(strings, Comparator.reverseOrder());
System.out.println(strings); // -> [charlie, bravo, alpha]

Benri.

warte eine Minute

Ist es nicht der aktuelle Code Java?

Strategiemuster

Ein Entwurfsmuster, das konzipiert wurde, weil die Funktion nicht als primäres Objekt verfügbar ist.

Mit Java können Sie Strategiemuster mithilfe von Polymorphismus mit Klassenmethodenüberschreibungen implementieren.

Ich werde die Details weglassen, aber es sieht so aus.

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    if (c.compare(a, b) < 0) {
        //a ist kleiner
    }else {
        //b ist kleiner
    }
}

Die Sortierregel wird gemäß der Implementierungsklasse der als Argument empfangenen Comparator-Schnittstelle bestimmt. Die Praxis, dass die Methode selbst nicht über ein Argument übergeben wird, sondern die Verarbeitung der Methode von außen über die Schnittstelle übergeben werden kann.

Der eigentliche Sortiervorgang sieht so aus jdk/TimSort.java at master · openjdk/jdk

Dieses Entwurfsmuster muss in Sprachen, in denen die Funktion ein primäres Objekt ist, nicht besonders auffällig sein.

Dieses Muster wird implizit in Sprachen verwendet, in denen die Funktion ein primäres Objekt ist.

Echt

Java kann Funktionen (Methoden) wie erstklassige Objekte mithilfe von Schnittstellen verarbeiten.

Was ist eine funktionale Schnittstelle?

Ab Java8 wird die Schnittstelle, mit der eine Funktion (Methode) als erstklassiges Objekt behandelt wird, wie der vorherige Komparator, als ** Funktionsschnittstelle ** bezeichnet. Da der Zweck darin besteht, Funktionen (Methoden) als erstklassige Objekte zu behandeln, gibt es nur eine abstrakte Methode für funktionale Schnittstellen.

Einige der in der Vergangenheit bereitgestellten Schnittstellen, wie z. B. Comparator und Runnable, haben gerade neue Namen erhalten. Die Funktion ist die gleiche wie bei einer normalen Schnittstelle. Die Anmerkung ** @ FunctionalInterface ** wurde der Funktionsschnittstelle hinzugefügt. e.g. java.lang.Runnable

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface {@code Runnable} is used
     * to create a thread, starting the thread causes the object's
     * {@code run} method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method {@code run} is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

jdk/FunctionalInterface.java at master · openjdk/jdk

@FunctionalInterface Anmerkungen für funktionale Schnittstellen hinzugefügt. Zur Laufzeit passiert nichts (die funktionale Schnittstelle ist funktional eine normale Schnittstelle, daher gibt es keine spezielle Verarbeitung). Beim Kompilieren wird geprüft, ob die Eigenschaften der Funktionsschnittstelle erfüllt sind.

Mit anderen Worten, es ist nicht unbedingt erforderlich, wenn die Funktionen der Funktionsschnittstelle verwendet werden. Da jedoch klar festgestellt werden kann, dass es sich eher um eine funktionale als um eine normale Schnittstelle handelt, sollte sie beim Erstellen einer funktionalen Schnittstelle hinzugefügt werden.

Standard-Funktionsschnittstelle

Bei der Behandlung einer Funktion (Methode) über eine Funktionsschnittstelle wird die zu behandelnde Methode vom Java-Typsystem beeinflusst. Insbesondere wenn im Folgenden verschiedene Methoden behandelt werden, müssen unterschiedliche Funktionsschnittstellen verwendet werden.

java8 bietet eine Standardfunktionsschnittstelle, die das Vorhandensein / Fehlen eines Rückgabewerts und den Typ eines Arguments abstrahiert und verfügbar macht. Grundsätzlich ist diese Schnittstelle ausreichend, und es wird gesagt, dass Sie die Verwendung dieser Schnittstelle in Betracht ziehen sollten. (Außer bei üblichen wie Runnable)

** Typische Standardfunktionsschnittstelle **

Name der Schnittstelle Name der abstrakten Methode Beispiel
Function<T,R> R apply(T t) Arrays::asList
Predicate boolean test(T t) Collections::isEmpty
Consumer<T> void accept(T t) System.out::println
Supplier<T> T get() Instant::now
UnaryOperator<T> T apply(T t) String::toLowerCase
BinaryOperator<T> T apply(T t1, T t2) BigInteger::add

java.util.function (Java Platform SE 8 )

Eigentlich verwenden

Erstellen wir einen Prozess zum Verarbeiten und Ausgeben einer Zeichenfolge. Beim Aufrufen des auszugebenden Prozesses wird die Zeichenfolge nach dem Bearbeitungsprozess ausgegeben, indem die Bearbeitungsprozessfunktion gemeinsam übergeben wird. Versuchen Sie, ein Programm zu erstellen, das nach der Konvertierung aller in Großbuchstaben ausgegeben wird.

Zeichenfolge-Anzeigeteil

Ausgabe nach Ausführung der übergebenen Funktion. Die Funktionsschnittstelle nimmt eine Funktion an, die eine Zeichenfolge vor der Änderung (Zeichenfolge) empfängt und die Zeichenfolge nach der Änderung (Zeichenfolge) zurückgibt und "UnaryOperator " verwendet.

static void print(String word, UnaryOperator<String> operator) {
    String newWord = operator.apply(word);
    System.out.println(newWord);
}

Funktion zu bestehen

Eine Funktion, die in Großbuchstaben konvertiert wird. Implementieren Sie "UnaryOperator ".

import java.util.function.UnaryOperator;

public class MyUpperOperator implements UnaryOperator<String> {
    @Override
    public String apply(String s) {
        return s.toUpperCase();
    }
}

main Wenn Sie die zuvor erstellte Kapitalkonvertierungsfunktion und die Zeichenfolge an die Druckmethode übergeben, werden sie in allen Großbuchstaben ausgegeben.

public static void main(String[] args) {
    UnaryOperator<String> upperOperator = new MyUpperOperator();
    print("hello world", upperOperator); // -> HELLO WORLD
}

Gut

Es verwendet eine funktionale Schnittstelle, aber es ist nur ein Strategiemuster.

Methodenreferenz

Im vorherigen Beispiel ist tatsächlich ein leistungsfähigerer Schreibstil möglich. Mit einer Funktion namens ** Methodenreferenz ** ist es möglich, eine Methode, deren Argument und Rückgabewert übereinstimmen, direkt zu übergeben, ohne explizit eine funktionale Schnittstelle zu implementieren. Im Fall des vorherigen Beispiels kann die Methode, da sie "UnaryOperator " ist, direkt als Methodenreferenz übergeben werden, wenn sie aus einem String besteht und der Rückgabewert "String" ist. Sie können die Methode toUpperCase () der String-Klasse direkt der Instanz UnaryOperator <String> zuweisen. (Bei einer Methodenreferenz einer Instanzmethode wird das Empfängerobjekt als erstes Argument interpretiert. Siehe die folgende Syntax.) In diesem Fall scheint der aufrufende Quellcode die Funktion als erstklassiges Objekt zu behandeln.

public static void main(String[] args) {
    // UnaryOperator<String> upperOperator = new MyUpperOperator();
    UnaryOperator<String> upperOperator = String::toUpperCase;
    print("hello world", upperOperator); // -> HELLO WORLD
}

Wie es funktioniert ist schwierig

Es scheint, dass es die Anweisung invokeDynamic der JVM verwendet. Es scheint, dass die Klasse zur Laufzeit dynamisch generiert und gelöst wird, nicht zur Kompilierungszeit.

Grammatik

Notation von zwei aufeinanderfolgenden Semikolons. Der auf beiden Seiten des Semikolons beschriebene Inhalt kann je nach Art der Methode grob in vier Muster unterteilt werden.

Ziel Grammatik Beispiel
Klassenmethode Name der Klasse::Name der Klassenmethode String::toString
Instanzmethode Instanzname::Name der Instanzmethode System.out::println
Instanzmethode * Name der Klasse::Name der Instanzmethode String::toUpperCase
Konstrukteur Name der Klasse::new String::new

Versuchen Sie es mit

Das vorige Beispiel kann sehr kurz beschrieben werden. Die MyUpperOperator-Klasse wird nicht mehr benötigt.

public static void main(String[] args) {
    print("hello world", String::toUpperCase); // -> HELLO WORLD
}

Außerdem erfordern viele der in Java8 hinzugefügten Stream-APIs eine funktionale Schnittstelle, sodass diese Art der Verarbeitung durchgeführt werden kann.

public static void main(String[] args) {
    List<String> strings = Arrays.asList("bravo", "charlie", "alpha");
    strings.stream()
            .map(String::toUpperCase)// Function:In Großbuchstaben konvertieren
            .forEach(System.out::println);// Consumer:Ausgabe
            // BRAVO
            // CHARLIE
            // ALPHA
}

Sehr praktisch, aber

Es ist notwendig, die Methode im Voraus zu deklarieren, um damit umgehen zu können. Es ist gut, die bereitgestellten Methoden zu verwenden, aber Sie müssen Ihre eigene Verarbeitung einzeln als Methode deklarieren. Ich möchte die Methode wie ein Literal behandeln.

Lambda-Stil

Bewertungsformelmechanismus, der einer Funktionsschnittstelle zugewiesen werden kann. Sie können eine Methode als Ausdruck schreiben, ohne die Methode zu deklarieren.

B. Funktion zum Hinzufügen eines Punkts zur Zeichenfolge

public static void main(String[] args) {
//    UnaryOperator<String> upperOperator = String::toUpperCase;
    UnaryOperator<String> periodOperator = (String s) -> { return s + "."; };
    print("hello world", periodOperator); // -> hello world.
}

Grammatik

Dies ist die Grundform (Formale Argumentzeichenfolge) -> {Verarbeitung der Body-Anweisung} e.g. (String s) -> { return s + "."; }

Verschiedene Beschreibungen können nur unter bestimmten Bedingungen weggelassen werden. Ich werde es weglassen, weil ich denke, dass verschiedene Dinge herauskommen werden, wenn ich es nachschlage. in diesem Fall UnaryOperator<String> periodOperator = s -> s + ".";

Versuchen Sie es mit

public static void main(String[] args) {
    print("hello world", s -> s + "."); // -> hello world.
}

public static void main(String[] args) {
    List<String> strings = Arrays.asList("bravo", "", "charlie", "alpha");
    strings.stream()
            .filter(s -> !s.isEmpty()) // Predicate:Leerzeichen entfernen
            .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1)) // Function:Konvertieren Sie nur das erste Zeichen in Großbuchstaben
            .forEach(System.out::println); // Consumer:Ausgabe
             //  Bravo
             //  Charlie
             //  Alpha
}

Ein Beispiel für Letzteres ist der am Anfang dieses Artikels erwähnte Prozess.

Sie können diesen Quellcode lesen und verwenden.

Zusammenfassung

Die funktionale Programmierung wurde seit Java8 verbessert.

Referenz

Überarbeitete 2. Ausgabe von Perfect Java: Book Guide | Technical Review Effektive Java 3rd Edition-Maruzen Publishing, ein spezialisierter Buchverlag für Wissenschaft, Technik, Medizin sowie Geistes- und Sozialwissenschaften

Recommended Posts

Java und primäre Funktionen - jenseits funktionaler Schnittstellen -
Java und JavaScript
XXE und Java
[Java] Funktionsschnittstelle
Effektive Java 3rd Edition Kapitel 4 Klassen und Schnittstellen
Getter und Setter (Java)
[Java] Thread und ausführbar
Java-Standardfunktionstyp-Schnittstelle
Java wahr und falsch
[Java] Vergleich von Zeichenketten und && und ||
Java - Serialisierung und Deserialisierung
[Java] Argumente und Parameter
timedatectl und Java TimeZone
[Java] Verzweigen und Wiederholen
[Java] Variablen- und Typtypen
Java (Klasse und Instanz)
[Java] Überladen und überschreiben
Studiere Java # 2 (\ mark and operator)
Java Version 8 und neuere Funktionen
[Java] Funktionsschnittstelle / Lambda-Ausdruck
[Java] Unterschied zwischen == und gleich
[Java] Stapelbereich und statischer Bereich
[Java] Generics-Klasse und Generics-Methode
Probieren Sie den Funktionstyp in Java aus! ①
Java-Programmierung (Variablen und Daten)
Java-Ver- und Entschlüsselung PDF
Java und Iterator Teil 1 Externe Iterator Edition
Java if- und switch-Anweisungen
Definition und Instanziierung von Java-Klassen
Apache Hadoop und Java 9 (Teil 1)
[Java] Über String und StringBuilder
[Java] HashCode und gleich Überschreibung
☾ Java / Repeat-Anweisung und Repeat-Steueranweisung
Java-Methoden und Methodenüberladungen
Java Generics T und? Unterschied
Vor- und Nachteile von Java
Java (bedingte Verzweigung und Wiederholung)
Implementieren Sie eine funktionsähnliche schnelle Sortierung in Java
Über Java-Paket und Import
[Java] Laden Sie ein Bild hoch und konvertieren Sie es in Base64
C # und Java überschreiben Story
Java abstrakte Methoden und Klassen
Java während und für Anweisungen
Java-Kapselung und Getter und Setter