Heim >类库下载 >java类库 >Eine vorläufige Studie zu Java-Lambda-Ausdrücken

Eine vorläufige Studie zu Java-Lambda-Ausdrücken

高洛峰
高洛峰Original
2016-10-09 10:24:261756Durchsuche

Eine vorläufige Studie zu Java-Lambda-Ausdrücken

Vorwort

Dieser Artikel wurde von Trisha Gees Keynote-Rede „Refactoring to Java 8“ auf der JavaOne 2016 inspiriert.

Java 8 wird seit mehr als zwei Jahren veröffentlicht, aber viele Leute verwenden immer noch JDK7. Für Unternehmen ist es nicht unbedingt eine schlechte Sache, bei der Technologie vorsichtig zu sein, aber für das persönliche Lernen gilt: Wenn Sie neue Technologien nicht erlernen, werden Sie wahrscheinlich von ihnen im Stich gelassen. Eine wichtige Änderung in Java 8 ist die Einführung des Lambda-Ausdrucks, der großartig klingt. Auch wenn ich nicht weiß, was Lambda-Ausdruck ist, finde ich ihn dennoch großartig. Keine Angst, auf Sprachebene ist der Lambda-Ausdruck nur eine neue Syntax. Damit öffnet Java die Tür zur funktionalen Programmierung.

Warum Sie Lambda-Ausdrücke benötigen

Machen Sie sich keine Gedanken darüber, was ein Lambda-Ausdruck und was funktionale Programmierung ist. Werfen wir zunächst einen Blick auf die Annehmlichkeiten, die die neuen Syntaxfunktionen von Java 8 mit sich bringen. Ich glaube, Sie werden sich daran erinnern.

Bevor es einen Lambda-Ausdruck gibt, müssen Sie zum Erstellen eines neuen Threads wie folgt schreiben:

new Thread(new Runnable(){    @Override
    public void run(){
        System.out.println("Thread run()");
    }
}).start();

Nachdem es einen Lambda-Ausdruck gibt, können Sie wie folgt schreiben:

new Thread(
        () -> System.out.println("Thread run()")
).start();

Wie Sie sehen, ist der bisher nutzlose Vorlagencode weg! Wie oben gezeigt, besteht eine häufige Verwendung von Lambda-Ausdrücken darin, (einige) anonyme innere Klassen zu ersetzen, aber die Rolle von Lambda-Ausdrücken ist nicht darauf beschränkt.

Das Prinzip des Lambda-Ausdrucks

Wenn Sie mit dem Lambda-Ausdruck noch nicht vertraut sind, finden Sie es möglicherweise magisch: Sie können eine Funktion direkt definieren, ohne den Namen einer Klasse oder Methode zu deklarieren. Tatsächlich ist dies jedoch nur ein kleiner Trick des Compilers, und das Prinzip dahinter ist nicht schwer zu verstehen. Im Folgenden sind mehrere mögliche Schreibformen von Lambda-Ausdrücken aufgeführt:

Runnable run = () -> System.out.println("Hello World");// 1ActionListener listener = event -> System.out.println("button clicked");// 2Runnable multiLine = () -> {// 3
    System.out.println("Hello ");
    System.out.println("World");
};
BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5

Aus dem obigen Beispiel können wir Folgendes ermitteln:

Lambda-Ausdrücke haben Typen, und die linke Seite der Zuweisungsoperation ist der Typ. Der Typ des Lambda-Ausdrucks ist tatsächlich der Typ der entsprechenden Schnittstelle.

Lambda-Ausdrücke können mehrere Codezeilen enthalten, und Sie müssen geschweifte Klammern verwenden, um den Codeblock einzuschließen, genau wie beim Schreiben eines Funktionskörpers.

Meistens kann der Typ in der Parameterliste eines Lambda-Ausdrucks weggelassen werden, wie in den Auflistungen 2 und 5 gezeigt. Dies ist auf den Typableitungsmechanismus von Javac zurückzuführen. Der Compiler kann Typinformationen basierend auf dem Kontext ableiten.

Tatsächlich ist jeder Lambda-Ausdruck die Abkürzung der ursprünglichen anonymen inneren Klasse, die eine funktionale Schnittstelle (Funktionsschnittstelle) implementiert. Die sogenannte funktionale Schnittstelle bezieht sich auf eine Schnittstelle mit der hinzugefügten Annotation @FunctionalInterface und nur einer darin enthaltenen Schnittstellenfunktion. Java ist eine stark typisierte Sprache. Unabhängig davon, ob sie explizit angegeben wird, muss jede Variable und jedes Objekt einen eindeutigen Typ haben. Wenn nicht explizit angegeben, versucht der Compiler, den Typ zu bestimmen. Der Typ des Lambda-Ausdrucks ist der Typ der entsprechenden Funktionsschnittstelle.

Lambda-Ausdrücke und Stream

Eine weitere wichtige Verwendung von Lambda-Ausdrücken ist die Verwendung mit Stream. Stream ist eine Folge von Elementen, die sequentielle und parallele Aggregationsoperationen unterstützen. Stream ist eine Folge von Elementen, die verschiedene Operationen für diese Elemente unterstützt. Diese Operationen werden durch Lambda-Ausdrücke angegeben. Sie können sich einen Stream als eine Ansicht einer Java-Sammlung vorstellen, genau wie ein Iterator eine Ansicht eines Containers ist (aber der Stream ändert den Inhalt des Containers nicht). Die folgenden Beispiele zeigen häufige Verwendungen von Stream.

Beispiel 1

Angenommen, Sie müssen eine Zeichenfolge, die mit einer Zahl beginnt, aus einer Zeichenfolgenliste auswählen und diese ausgeben. Vor Java 7 mussten Sie Folgendes schreiben:

List<String> list = Arrays.asList("1one", "two", "three", "4four");for(String str : list){    if(Character.isDigit(str.charAt(0))){
        System.out.println(str);
    }
}

Und Java 8 kann so geschrieben werden:

List<String> list = Arrays.asList("1one", "two", "three", "4four");
list.stream()// 1.得到容器的Steam
    .filter(str -> Character.isDigit(str.charAt(0)))// 2.选出以数字开头的字符串
    .forEach(str -> System.out.println(str));// 3.输出字符串

Der obige Code zuerst 1. Ruft die List.stream()-Methode auf, um den Stream des Containers abzurufen, 2. Ruft dann den Filter auf( )-Methode zum Herausfiltern von Zeichenfolgen, die mit Zahlen beginnen. 3. Rufen Sie abschließend die Methode forEach() auf, um das Ergebnis auszugeben.

Die Verwendung von Stream hat zwei offensichtliche Vorteile:

Es reduziert den Vorlagencode und verwendet nur Lambda-Ausdrücke, um die erforderlichen Operationen anzugeben. Die Codesemantik ist klarer und einfacher zu lesen.

Ändern Sie die externe Iteration in die interne Iteration von Stream, wodurch die JVM selbst den Iterationsprozess optimieren kann (z. B. kann sie parallel iteriert werden).

Beispiel 2

Angenommen, Sie müssen alle Zeichenfolgen, die nicht mit einer Zahl beginnen, aus einer Liste von Zeichenfolgen auswählen, sie in Großbuchstaben umwandeln und die Ergebnisse in eine neue Sammlung einfügen. Der in Java 8 geschriebene Code lautet wie folgt:

List<String> list = Arrays.asList("1one", "two", "three", "4four");
Set<String> newList =
        list.stream()// 1.得到容器的Stream
        .filter(str -> !Character.isDigit(str.charAt(0)))// 2.选出不以数字开头的字符串
        .map(String::toUpperCase)// 3.转换成大写形式
        .collect(Collectors.toSet());// 4.生成结果集

上述代码首先1. 调用List.stream()方法得到容器的Stream,2. 然后调用filter()方法选出不以数字开头的字符串,3. 之后调用map()方法将字符串转换成大写形式,4. 最后调用collect()方法将结果转换成Set。这个例子还向我们展示了方法引用(method references,代码中标号3处)以及收集器(Collector,代码中标号4处)的用法,这里不再展开说明。

通过这个例子我们看到了Stream链式操作,即多个操作可以连成一串。不用担心这会导致对容器的多次迭代,因为不是每个Stream的操作都会立即执行。Stream的操作分成两类,一类是中间操作(intermediate operations),另一类是结束操作(terminal operation),只有结束操作才会导致真正的代码执行,中间操作只会做一些标记,表示需要对Stream进行某种操作。这意味着可以在Stream上通过关联多种操作,但最终只需要一次迭代。如果你熟悉Spark RDD,对此应该并不陌生。

结语

Java 8引入Lambda表达式,从此打开了函数式编程的大门。如果你之前不了解函数式编程,不必纠结于这个概念。编程过程中简洁明了的书写形式以及强大的Stream API会让你很快熟悉Lambda表达式的。

本文只对Java Lambda表达式的基本介绍,希望能够激发读者对Java函数式编程的兴趣。如果本文能够让你觉得Lambda表达式很好玩,函数式编程很有趣,并产生了进一步学习的欲望,那就再好不过了。文末参考文献中列出了一些有用的资源。


参考文献

http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
http://www.slideshare.net/trishagee/refactoring-to-java-8-devoxx-uk
《Java 8函数式编程 [英]沃伯顿》
https://www.oracle.com/javaone/speakers.html#gee


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn