讀Java實戰(第二版)筆記02_行為參數化Lambda表達式 讀Java實戰(第二版)筆記02_行為參數化Lambda表達式
1.行為參數化
1.1.處理頻繁變更的需求的一種軟體開發模式
1.1.1.不管你做什麼,使用者的需求肯定會變
1.1.2.可讓代碼更好地适應不斷變化的要求,減輕未來的工作量
1.2.一個方法接受多個不同的行為作為參數,并在内部使用它們,完成不同行為的能力
1.2.1.類似于在内聯“傳遞代碼”
1.2.2.重複使用同一個方法,給它不同的行為來達到不同的目的
1.2.2.1.DRY(Don't Repeat Yourself,不要重複自己)
1.2.2.2.例如:把疊代要篩選的集合的邏輯與對集合中每個元素應用的行為區分開來
1.3.傳遞代碼就是将新行為作為參數傳遞給方法
1.3.1.在Java 8之前可以用匿名類
1.3.1.1.匿名類和局部類(塊中定義的類)差不多,但匿名類沒有名字
1.3.1.2.GUI應用程式中經常使用匿名類來建立事件處理器對象
1.3.1.3.聲明很多隻要執行個體化一次的類
1.3.2.以用不同行為進行參數化的方法,包括排序、線程和GUI處理
2.Lambda表達式
2.1.基本文法
2.1.1.塊-風格的Lambda
2.1.1.1.(parameters) -> { statements; }
2.1.2.表達式-風格的Lambda
2.1.2.1.(parameters) -> expression
2.2.沒有聲明名稱的方法
2.3.和匿名類一樣,也能作為參數傳遞給一個方法
2.4.允許你直接以内聯的形式為函數式接口的抽象方法提供實作,并把整個表達式作為函數式接口的執行個體
2.5.一種簡潔的可傳遞匿名函數:它沒有名稱,但它有參數清單、函數主體、傳回類型,可能還有一個可以抛出的異常清單
2.5.1.匿名
2.5.2.函數
2.5.2.1.有參數清單、函數主體、傳回類型,還可能有可以抛出的異常清單
2.5.3.傳遞
2.5.3.1.可以作為參數傳遞給方法或存儲在變量中
2.5.4.簡潔
2.5.4.1.無須像匿名類那樣寫很多模闆代碼
2.6.方法調用的傳回值為空時,Java語言規範有一條特殊的規定,不需要使用括号環繞傳回值為空的單行方法調用
2.7.捕獲Lambda允許使用自由變量(不是參數,而是在外層作用域中定義的變量),就像匿名類一樣
2.7.1.可以沒有限制地捕獲(也就是在其主體中引用)執行個體變量和靜态變量
2.7.2.局部變量必須顯式聲明為final,或事實上是final
2.7.2.1.隻能捕獲指派給它們的局部變量一次
2.7.2.2.捕獲執行個體變量可以被看作捕獲最終局部變量this
2.7.2.3.不能修改定義Lambda的方法的局部變量的内容。這些變量必須是隐式最終的
2.7.3.執行個體變量都存儲在堆中
2.7.3.1.堆是線上程之間共享的
2.7.4.局部變量則儲存在棧上
2.7.4.1.允許捕獲可改變的局部變量,就會引發造成線程不安全的新的可能性
2.7.4.2.在通路自由局部變量時,實際上是在通路它的副本,而不是通路基本變量
2.7.5.不鼓勵你使用改變外部變量的典型指令式程式設計模式
2.8.Lambda是對值封閉,而不是對變量封閉
2.9.閉包就是一個函數的執行個體,且它可以無限制地通路那個函數的非本地變量
2.9.1.可以通路和修改其作用域之外的變量
3.匿名内部類
3.1.也可以完成Lambda表達式同樣的事情,比較笨拙:需要提供一個實作,然後再直接内聯将它執行個體化
4.方法引用
4.1.僅調用特定方法的Lambda的一種快捷寫法
4.1.1.更易讀
4.1.2.更自然
4.2.僅涉及單一方法的Lambda的文法糖
4.2.1.同樣的事情時要寫的代碼更少
4.3.如果一個Lambda代表的隻是“直接調用這個方法”,那最好還是用名稱來調用它,而不是去描述如何調用它
4.4.指向靜态方法的方法引用
4.4.1.例如Integer的parseInt方法,寫作Integer::parseInt
4.5.指向任意類型執行個體方法的方法引用
4.5.1.例如String的length方法,寫作String::length
4.6.指向現存對象或表達式執行個體方法的方法引用
4.6.1.例如你有一個局部變量expensive Transaction儲存了Transaction類型的對象,它提供了執行個體方法getValue,那你就可以這麼寫expensive-Transaction::getValue
4.7.構造函數引用
4.7.1.對于一個現有構造函數,你可以利用它的名稱和關鍵字new來建立它的一個引用:ClassName::new
4.7.2.不将構造函數執行個體化卻能夠引用它
4.7.3.語言本身并沒有提供的函數式接口,可以自己建立一個
5.函數式接口
5.1.隻定義一個抽象方法的接口
5.1.1.很多預設方法,隻要接口隻定義了一個抽象方法,仍然是一個函數式接口
5.1.2.預設方法不是抽象方法
5.2.函數式接口的抽象方法的簽名稱為函數描述符
5.2.1.基本上就是Lambda表達式的簽名
5.3.@FunctionalInterface
5.3.1.這個标注用于表示該接口會設計成一個函數式接口,是以對文檔來說非常有用
5.3.2.不是必需的,但對于為此設計的接口而言,使用它是比較好的做法
5.4.java.util.function包中新的函數式接口
5.4.1.Predicate接口定義了一個名叫test的抽象方法,它接受泛型T對象,并傳回一個boolean
5.4.1.1.謂詞(即一個傳回boolean值的函數)
5.4.2.Consumer接口定義了一個名叫accept的抽象方法,它接受泛型T的對象,沒有傳回(void)
5.4.3.Function<T, R>接口定義了一個叫作apply的抽象方法,它接受泛型T的對象,并傳回一個泛型R的對象
5.4.3.1.配了andThen和compose兩個預設方法,它們都會傳回Function的一個執行個體
5.4.4.Supplier
5.4.5.BinaryOperator
5.5.函數式接口中的任何一個都不允許抛出受檢異常(checked exception)
5.5.1.定義一個自己的函數式接口,并聲明受檢異常
5.5.2.把Lambda包在一個try/catch塊中
5.5.2.1.環繞執行(execute around)模式
6.裝箱(boxing)
6.1.将基本類型轉換為對應的引用類型的機制
7.拆箱(unboxing)
7.1.将引用類型轉換為對應的基本類型
8.自動裝箱機制
8.1.裝箱和拆箱操作是自動完成的
9.特殊處理
9.1.IntPredicate
9.2.DoublePredicate
9.3.ToIntFunction
9.4.IntConsumer
9.5.LongBinaryOperator
9.6.IntFunction
9.7.IntToDoubleFunction
10.目标類型
10.1.Lambda的類型是從使用Lambda的上下文推斷出來的
10.2.上下文(比如,接受它傳遞的方法的參數,或接受它的值的局部變量)中Lambda表達式需要的類型
10.3.同一個Lambda表達式就可以與不同的函數式接口聯系起來,隻要它們的抽象方法簽名能夠相容
10.4.Lambda表達式的上下文是Object(目标類型)。但Object不是一個函數式接口。
10.4.1.你可以把目标類型改成Runnable,它的函數描述符是() -> void
10.5.為了消除顯式的二義性,你可以對Lamda進行強制類型轉換
10.6.有時候顯式寫出類型更易讀,有時候去掉它們更易讀