[書]java8函數語言程式設計(1)
阿新 • • 發佈:2019-01-23
一:Learning
package testLambda;
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.util.EventListener;
import java.util.function.BinaryOperator;
import org.junit.Test;
/**
* @author zhangdi
* @description Lambda
*/
public class LambdaChapter1and2 {
public static void main(String[] args) {
}
/**
* 2.1 辨別Lambda表示式
*/
@SuppressWarnings("unused")
@Test
public static void recognizeLambda() {
// 1.不包含引數,使用()表示,沒有引數,該Lamdba表示式實現了Runnable介面,該介面也只有一個run方法,沒有引數,返回型別為void
Runnable NoArguments = () -> System.out.println("hello world");
// 2.該Lamdba表示式包含且只包含一個引數可省略引數的括號
ActionListener oneArgument = (event) -> System.out.println("button clicked" );
ActionListener oneArgument2 = event -> System.out.println("button clicked");
// 3.可以使用{}將lamdba表示式的主體括起來;只有一行程式碼的lambda的表示式也可以使用大括號;用來明確表示式的開始與結束
Runnable multistatement = () -> {
System.out.println("hello");
System.out.println("world");
};
// 4.lambda表示式也可以是包含多個引數的方法;
BinaryOperator<Long> add = (x, y) -> x + y;
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
}
/**
* 2.2 函式介面是隻有一個抽象方法的介面,用作lambda表示式的型別
*/
public static void 函式介面() {
}
/**
* 2.3 引用值,而不是變數
* 既成事實上的 final 是指只能給該變數賦值一次。 換句話說, Lambda 表示式引用的是值,而不是變數
* Lambda 表示式中引用既成事實上的 final 變數 -->Lambda 表示式都是靜態型別
*/
public static void valueReference() {
Button button = new Button();
String name = getUserName();
button.addActionListener(event -> System.out.println("hi " + name));
}
private static String getUserName() {
// TODO Auto-generated method stub
return "test";
}
/**
* 2.4 ActionListener 介面: 接受 ActionEvent 型別的引數, 返回空
*
* ActionListener 只有一個抽象方法: actionPerformed, 被用來表示行為: 接受一個引數, 返回空。
* 記住, 由於 actionPerformed 定義在一個接口裡, 因此 abstract 關鍵字不是必需 的。
* 該介面也繼承自一個不具有任何方法的父介面: EventListener。
*/
public interface ActionListener extends EventListener {
public void actionPerformed(ActionEvent event);
}
/**
*java中重要的函式介面
* 介面 引數 返回型別 示例
* Predicate<T> T boolean 這張唱片已經發行了嗎
* Consumer<T> T void 輸出一個值
* Function<T,R> T R 獲得 Artist 物件的名字
* Supplier<T> None T 工廠方法
* UnaryOperator<T> T T 邏輯非( !)
* BinaryOperator<T> (T, T) T 求兩個數的乘積( *)
*
* 2.5型別推斷
* Predicate 用來判斷真假的函式介面
*/
public static void 型別推斷() {
Predicate<Integer> atLeast5 = x -> x > 5;
}
//Predicate 介面的原始碼, 接受一個物件, 返回一個布林值
public interface Predicate<T> {
boolean test(T t);
}
//略顯複雜的型別推斷 :型別推斷系統相當智慧, 但若資訊不夠, 型別推斷系統也無能為力。 型別系統不會漫無邊
//際地瞎猜, 而會中止操作並報告編譯錯誤, 尋求幫助 ,如去掉Long,程式碼不會通過編譯
BinaryOperator<Long> addLongs = (x, y) -> x + y;
//沒有泛型, 程式碼則通不過編譯
}
java中重要的函式介面:
總結:
* Lambda 表示式是一個匿名方法, 將行為像資料一樣進行傳遞。
* Lambda 表示式的常見結構: BinaryOperator add = (x, y) → x + y。
* Lambda表示式裡引用的到的變數是final的,本質是值,不是變數。
* Lambda表示式的型別是個函式介面,即僅有一個抽象方法的介面。( 函式介面指僅具有單個抽象方法的介面, 用來表示 Lambda 表示式的型別。)
二: 練習
1. 請看例 2-15 中的 Function 函式介面並回答下列問題。
例 2-15 Function 函式介面
public interface Function<T, R> {
R apply(T t);
}
a. 請畫出該函式介面的圖示。
b. 若要編寫一個計算器程式, 你會使用該介面表示什麼樣的 Lambda 表示式?
c. 下列哪些 Lambda 表示式有效實現了 Function<Long,Long> ?
x -> x + 1;
(x, y) -> x + 1;
x -> x == 1;
2. ThreadLocal Lambda 表示式。 Java 有一個 ThreadLocal 類, 作為容器儲存了當前執行緒裡
區域性變數的值。 Java 8 為該類新加了一個工廠方法, 接受一個 Lambda 表示式, 併產生
一個新的 ThreadLocal 物件, 而不用使用繼承, 語法上更加簡潔。
a. 在 Javadoc 或整合開發環境( IDE) 裡找出該方法。
b. DateFormatter 類是非執行緒安全的。 使用建構函式建立一個執行緒安全的 DateFormatter
物件, 並輸出日期, 如“ 01-Jan-1970”。
3. 型別推斷規則。 下面是將 Lambda 表示式作為引數傳遞給函式的一些例子。 javac 能正
確推斷出 Lambda 表示式中引數的型別嗎? 換句話說, 程式能編譯嗎?
a. Runnable helloWorld = () -> System.out.println("hello world");
b. 使用 Lambda 表示式實現 ActionListener 介面:
JButton button = new JButton();
button.addActionListener(event ->
System.out.println(event.getActionCommand()));
c. 以如下方式過載 check 方法後, 還能正確推斷出 check(x -> x > 5) 的型別嗎?
interface IntPred {
boolean test(Integer value);
}
boolean check(Predicate<Integer> predicate);
boolean check(IntPred predicate);
練習答案:
public class Chapter1And2_practice {
public static class Question1 {
//x -> x + 1;
}
public static class Question3 {
//a.yes
//b.yes
//c.no
}
/**
* @author NSNP736
* @description Question2
*/
public static class Question2 {
//lamada-by richard
public final static ThreadLocal<DateFormatter> formatter = ThreadLocal.withInitial(() -> new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy")));
//Anonymous Inner Class -byzhangdi
public final static ThreadLocal<DateFormatter> formatter2 = ThreadLocal.withInitial( new Supplier<DateFormatter>(){
@Override
public DateFormatter get() {
DateFormatter dateFormatter = new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy"));
return dateFormatter;
}});
}
@Test
public void exampleInB() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1970);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
//formatter
String format1 = Question2.formatter.get().getFormat().format(cal.getTime());
//formatter2
ThreadLocal<DateFormatter> formatter = Question2.formatter;
DateFormatter dateFormatter = formatter.get();
Format format = dateFormatter.getFormat();
String format2 = format.format(cal.getTime());
assertEquals("01-一月-1970", format1);
assertEquals("01-一月-1970", format2);
}
}