你真的瞭解java的lambda嗎?- java lambda用法與源碼分析

你真的瞭解java的lambda嗎?- java lambda用法與源碼分析

轉載請註明來源:cmlanche.comjava

示例:最廣泛的一個例子,執行一個線程

new Thread(() -> System.out.print("hello world")).start();

->咱們發現它指向的是Runnable接口express

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

分析

  1. ->這個箭頭是lambda表達式的關鍵操做符編程

  2. ->把表達式分紅兩截,前面是函數參數,後面是函數體。app

  3. Thread的構造函數接收的是一個Runnable接口對象,而咱們這裏的用法至關因而把一個函數當作接口對象傳遞進去了,這點理解很關鍵,這正是函數式編程的含義所在。less

  4. 咱們注意到Runnable有個註解@FunctionalInterface,它是jdk8才引入,它的含義是函數接口。它是lambda表達式的協議註解,這個註解很是重要,後面作源碼分析會專門分析它的官方註釋,到時候一目瞭然。ide

    /* @jls 4.3.2. The Class Object
    * @jls 9.8 Functional Interfaces
    * @jls 9.4.3 Interface Method Body
    * @since 1.8
    */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface FunctionalInterface {}

由此引起的一些案例

有參數有返回值的實例:集合排序

List<String> list = new ArrayList<>();
Collections.sort(list, (o1, o2) -> {
if(o1.equals(o2)) {
return 1;
}
return -1;
})

咱們知道Collections.sort方法的第二個參數接受的是一個Comparator<T>的對象,它的部分關鍵源碼是這樣的:函數式編程

@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}

如上已經去掉註釋和部分其餘方法。函數

咱們能夠看到sort的第二個參數是Comparator的compare方法,參數類型是T,分別是o1和o2,返回值是一個int。工具

疑問

  1. 上面的示例咱們看到接口都有個@FunctionalInterface的註解,可是咱們在實際編程中並無加這個註解也能夠實現lambda表達式,例如:源碼分析

    public class Main {

    interface ITest {
    int test(String string);
    }

    static void Print(ITest test) {
    test.test("hello world");
    }

    public static void main(String[] args) {
    Print(string -> {
    System.out.println(string);
    return 0;
    });
    }
    }

    如上所示,確實不須要增長@FunctionInterface註解就能夠實現

  2. 若是在1中的示例的ITest接口中增長另一個接口方法,咱們會發現不能再用lambda表達式。

咱們帶着這兩個疑問來進入源碼解析。

源碼解析

必須瞭解註解 @FunctionInterface

上源碼:

package java.lang;

import java.lang.annotation.*;

/**
* An informative annotation type used to indicate that an interface
* type declaration is intended to be a <i>functional interface</i> as
* defined by the Java Language Specification.
*
* Conceptually, a functional interface has exactly one abstract
* method. Since {@linkplain java.lang.reflect.Method#isDefault()
* default methods} have an implementation, they are not abstract. If
* an interface declares an abstract method overriding one of the
* public methods of {@code java.lang.Object}, that also does
* <em>not</em> count toward the interface's abstract method count
* since any implementation of the interface will have an
* implementation from {@code java.lang.Object} or elsewhere.
*
* <p>Note that instances of functional interfaces can be created with
* lambda expressions, method references, or constructor references.
*
* <p>If a type is annotated with this annotation type, compilers are
* required to generate an error message unless:
*
* <ul>
* <li> The type is an interface type and not an annotation type, enum, or class.
* <li> The annotated type satisfies the requirements of a functional interface.
* </ul>
*
* <p>However, the compiler will treat any interface meeting the
* definition of a functional interface as a functional interface
* regardless of whether or not a {@code FunctionalInterface}
* annotation is present on the interface declaration.
*
* @jls 4.3.2. The Class Object
* @jls 9.8 Functional Interfaces
* @jls 9.4.3 Interface Method Body
* @since 1.8
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

咱們說過這個註解用來規範lambda表達式的使用協議的,那麼註釋中都說了哪些呢?

  1. 一種給interface作註解的註解類型,被定義成java語言規範

    * An informative annotation type used to indicate that an interface
    * type declaration is intended to be a <i>functional interface</i> as
    * defined by the Java Language Specification.
  2. 一個被它註解的接口只能有一個抽象方法,有兩種例外。

    1. 第一是接口容許有實現的方法,這種實現的方法是用default關鍵字來標記的(java反射中java.lang.reflect.Method#isDefault()方法用來判斷是不是default方法),例如:

    image-20180722102331347

    ​ 固然這是jdk8才引入的特性,到此咱們才知道,知識是一直在變化的,咱們在學校中學到interface接口不容許有實現的方法是錯誤的,隨着時間推移,一切規範都有可能發生變化

    1. 若是聲明的方法和java.lang.Object中的某個方法同樣,它能夠不當作未實現的方法,不違背這個原則:一個被它註解的接口只能有一個抽象方法

      例如一樣是Compartor接口中,它從新聲明瞭equals方法:

      image-20180722102904828

    這些是對以下注釋的翻譯和解釋

    * Conceptually, a functional interface has exactly one abstract
    * method. Since {@linkplain java.lang.reflect.Method#isDefault()
    * default methods} have an implementation, they are not abstract. If
    * an interface declares an abstract method overriding one of the
    * public methods of {@code java.lang.Object}, that also does
    * <em>not</em> count toward the interface's abstract method count
    * since any implementation of the interface will have an
    * implementation from {@code java.lang.Object} or elsewhere.
  3. 若是一個類型被這個註解修飾,那麼編譯器會要求這個類型必須知足以下條件

    1. 這個類型必須是一個interface,而不是其餘的註解類型、枚舉enum或者類class
    2. 這個類型必須知足function interface的全部要求,如你個包含兩個抽象方法的接口增長這個註解,會有編譯錯誤。
    * <p>If a type is annotated with this annotation type, compilers are
    * required to generate an error message unless:
    *
    * <ul>
    * <li> The type is an interface type and not an annotation type, enum, or class.
    * <li> The annotated type satisfies the requirements of a functional interface.
    * </ul>
  4. 編譯器會自動把知足function interface要求的接口自動識別爲function interface,因此你纔不須要對上面示例中的ITest接口增長@FunctionInterface註解。

    * <p>However, the compiler will treat any interface meeting the
    * definition of a functional interface as a functional interface
    * regardless of whether or not a {@code FunctionalInterface}
    * annotation is present on the interface declaration.

經過了解function interface咱們可以知道怎麼才能正確的建立一個function interface來作lambda表達式了。接下來的是瞭解java是怎麼把一個函數當作一個對象做爲參數使用的。

穿越:對象變身函數

讓咱們從新覆盤一下上面最開始的實例:

new Thread(() -> System.out.print("hello world")).start();

咱們知道在jdk8之前咱們都是這樣來執行的:

Runnable r = new Runnable(){
System.out.print("hello world");
};
new Thread(r).start();

咱們知道二者是等價的,也就是說r 等價於()->System.out.print("hello world"),一個接口對象等於一個lambda表達式?那麼lambda表達式確定作了這些事情(未看任何資料,純粹推理,有誤再改正):

  1. 建立接口對象
  2. 實現接口對象
  3. 返回接口對象

關於UnaryOperator

上篇文章(聊一聊JavaFx中的TextFormatter以及一元操做符UnaryOperator)關於UnaryOperator草草收尾,在這裏給你們從新梳理一下,關於它的使用場景以及它與lambda表達式的關係

使用場景

要先理解它的做用,它是接受一個參數並返回與該類型同的值,來看一個List怎麼用它的,java.util.List中的replaceAll就用它了:

default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}

咱們能夠看到這個方法的目的是把list中的值通過operator操做後從新返回一個新值,例如具體調用

List<String> list = new ArrayList<>();
list.add("abc");
list.replaceAll(s -> s + "efg");

System.out.println(list);

其中lambda表達式s->s+"efg"就是這個operator對象,那麼最終list中的值就變成了[「abcefg」],由此咱們能夠知道它的做用就是對輸入的值再加工,並返回同類型的值,怎麼用就須要你本身擴展發揮了。

與lambda表達式的關係?

在我看來,它跟lambda表達式的關係並不大,只是它是jdk內置的一種標準操做,相似的二元操做符BinaryOperator它能夠接受兩個同類型參數,並返回同類型參數的值。

關於UnaryOperator,咱們百尺竿頭更進一步,深刻到核心

先貼出它的源碼:

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

/**
* Returns a unary operator that always returns its input argument.
*
* @param <T> the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}

咱們看到這個function interface竟然沒有抽象方法,不,不是沒有,咱們繼續看Function接口

@FunctionalInterface
public interface Function<T, R> {

/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);

/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}

既然他們都被註解爲@FunctionInterface了,那麼他們確定有一個惟一的抽象方法,那就是apply

咱們知道->lambda表達式它是不須要關心函數名字的,因此無論它叫什麼,apply也好,apply1也好均可以,但jdk確定要叫一個更加合理的名字,那麼咱們知道s -> s + "efg"->調用的就是apply方法

並且咱們注意到這裏有一個identity()的靜態方法,它返回一個Function對象,它其實跟lambda表達式關係也不大,它的做用是返回當前function所要表達的lambda含義。至關於建立了一個自身對象。

Function算是lambda的一種擴展應用,這個Function的的做用是Represents a function that accepts one argument and produces a result.意思是接受一個參數,併產生(返回)一個結果(類型可不一樣)。

相似的還有不少Function,都在包java.util.Function中

image-20180722115019213

你也能夠建立本身的Function,它是用來表達操做是怎樣的。如傳入的參數是什麼,返回的是什麼。

其實你只要明白它抽象的是操做就能夠了。

到此就知道,原來UnaryOperator沒啥神祕的,jdk把這些操做放在java.util.function中也正說明了它是一個工具類,是爲了提取重複代碼,讓它能夠重用,畢竟須要用到這樣的操做的地方太多了,提取是有必要的。

相關文章
相關標籤/搜索