示例:最廣泛的一個例子,執行一個線程
new Thread(() -> System.out.print("hello world")).start(); |
->
咱們發現它指向的是Runnable
接口express
|
分析
-
->
這個箭頭是lambda表達式的關鍵操做符編程 -
->
把表達式分紅兩截,前面是函數參數,後面是函數體。app -
Thread的構造函數接收的是一個Runnable接口對象,而咱們這裏的用法至關因而把一個函數當作接口對象傳遞進去了,這點理解很關鍵,這正是函數式編程的含義所在。less
-
咱們注意到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
*/
由此引起的一些案例
有參數有返回值的實例:集合排序
List<String> list = new ArrayList<>(); |
咱們知道Collections.sort方法的第二個參數接受的是一個Comparator<T>
的對象,它的部分關鍵源碼是這樣的:函數式編程
|
如上已經去掉註釋和部分其餘方法。函數
咱們能夠看到sort的第二個參數是Comparator的compare方法,參數類型是T,分別是o1和o2,返回值是一個int。工具
疑問
-
上面的示例咱們看到接口都有個
@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
註解就能夠實現 -
若是在1中的示例的ITest接口中增長另一個接口方法,咱們會發現不能再用lambda表達式。
咱們帶着這兩個疑問來進入源碼解析。
源碼解析
必須瞭解註解 @FunctionInterface
上源碼:
package java.lang; |
咱們說過這個註解用來規範lambda表達式的使用協議的,那麼註釋中都說了哪些呢?
-
一種給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. -
一個被它註解的接口只能有一個抽象方法,有兩種例外。
- 第一是接口容許有實現的方法,這種實現的方法是用default關鍵字來標記的(java反射中java.lang.reflect.Method#isDefault()方法用來判斷是不是default方法),例如:
固然這是jdk8才引入的特性,到此咱們才知道,知識是一直在變化的,咱們在學校中學到interface接口不容許有實現的方法是錯誤的,隨着時間推移,一切規範都有可能發生變化。
-
若是聲明的方法和java.lang.Object中的某個方法同樣,它能夠不當作未實現的方法,不違背這個原則:一個被它註解的接口只能有一個抽象方法
例如一樣是Compartor接口中,它從新聲明瞭equals方法:
這些是對以下注釋的翻譯和解釋
* 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 { -
若是一個類型被這個註解修飾,那麼編譯器會要求這個類型必須知足以下條件
- 這個類型必須是一個interface,而不是其餘的註解類型、枚舉enum或者類class
- 這個類型必須知足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> -
編譯器會自動把知足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 {
經過了解function interface咱們可以知道怎麼才能正確的建立一個function interface來作lambda表達式了。接下來的是瞭解java是怎麼把一個函數當作一個對象做爲參數使用的。
穿越:對象變身函數
讓咱們從新覆盤一下上面最開始的實例:
new Thread(() -> System.out.print("hello world")).start(); |
咱們知道在jdk8之前咱們都是這樣來執行的:
Runnable r = new Runnable(){ |
咱們知道二者是等價的,也就是說r
等價於()->System.out.print("hello world")
,一個接口對象等於一個lambda表達式?那麼lambda表達式確定作了這些事情(未看任何資料,純粹推理,有誤再改正):
- 建立接口對象
- 實現接口對象
- 返回接口對象
關於UnaryOperator
上篇文章(聊一聊JavaFx中的TextFormatter以及一元操做符UnaryOperator)關於UnaryOperator
草草收尾,在這裏給你們從新梳理一下,關於它的使用場景以及它與lambda表達式的關係
使用場景
要先理解它的做用,它是接受一個參數並返回與該類型同的值,來看一個List怎麼用它的,java.util.List中的replaceAll就用它了:
default void replaceAll(UnaryOperator<E> operator) { |
咱們能夠看到這個方法的目的是把list中的值通過operator操做後從新返回一個新值,例如具體調用
List<String> list = new ArrayList<>(); |
其中lambda表達式s->s+"efg"
就是這個operator對象,那麼最終list中的值就變成了[「abcefg」],由此咱們能夠知道它的做用就是對輸入的值再加工,並返回同類型的值,怎麼用就須要你本身擴展發揮了。
與lambda表達式的關係?
在我看來,它跟lambda表達式的關係並不大,只是它是jdk內置的一種標準操做,相似的二元操做符BinaryOperator
它能夠接受兩個同類型參數,並返回同類型參數的值。
關於UnaryOperator,咱們百尺竿頭更進一步,深刻到核心
先貼出它的源碼:
|
咱們看到這個function interface竟然沒有抽象方法,不,不是沒有,咱們繼續看Function接口
|
既然他們都被註解爲@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中
你也能夠建立本身的Function,它是用來表達操做是怎樣的。如傳入的參數是什麼,返回的是什麼。
其實你只要明白它抽象的是操做就能夠了。
到此就知道,原來UnaryOperator沒啥神祕的,jdk把這些操做放在java.util.function中也正說明了它是一個工具類,是爲了提取重複代碼,讓它能夠重用,畢竟須要用到這樣的操做的地方太多了,提取是有必要的。