lambda表達式
可以把Lambda表達式理解為簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它 有參數列表、函數主體、返回類型,可能還有一個可以拋出的異常列表。
特點
- 匿名:它不像普通的方法那樣有一個明確的名稱:寫得少而想 得多!
- 函數:為Lambda函數不像方法那樣屬于某個特定的類。但和方 法一樣,Lambda有參數列表、函數主體、返回類型,還可能有可以拋出的異常列表。
- 傳遞:Lambda表達式可以作為參數傳遞給方法或存儲在變量中。
- 簡潔:無需像匿名類那樣寫很多模板代碼。
基本語法
(parameters) -> expression
或者
(parameters) -> { statements; }
使用示例
使用案例 | Lambda示例 |
---|---|
布爾表達式 | (List<String> list) -> list.isEmpty() |
創(chuàng)建對象 | () -> new Apple(10) |
消費一個對象 | (Apple a) -> { System.out.println(a.getWeight()); } |
從一個對象中選擇/抽取 | (String s) -> s.length() |
組合兩個值 | (int a, int b) -> a * b |
比較兩個對象 | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) |
哪里使用lambda?
前提
行為參數化
行為參數化就是可以幫助你處理頻繁變更的需求的一種軟件開發(fā)模式。
讓方法接受多種行為(或戰(zhàn) 略)作為參數,并在內部使用,來完成不同的行為。
比如:
你的室友知道怎么開車去超市,再開回家。于是你可以告訴他去買一些東西,比如面包、奶酪、葡萄酒什么的。這相當于調用一goAndBuy 方法,把購物單作為參數。然而,有一天你在上班,你需要他去做一件他從來沒有做過的事情:從郵局取一個包裹。現在你就需要傳遞給他一系列指示了:去郵局,使用單號,和工作人員說明情況,取走包裹。你可以把這些指示用電子郵件發(fā)給他,當他收到之后就可以按照指示行事了。你現在做的事情就更高級一些了,相當于一個方法: go ,它可以接受不同的新行為作為參數,然后去執(zhí)行。
打車回家,當司機不知道怎么走的時候,不止要告訴目的地,還要告訴怎么走。
過濾蘋果方法
private List<Apple> filterApples(List<Apple> apples, Predicate<Apple> applePerdicate){
List<Apple> filterApple = new ArrayList<>();
for(Apple apple : apples) {
if(applePerdicate.test(apple)){
filterApple.add(apple);
}
}
return filterApple;
}
調用
List<Apple> appleList = filterApples(apples, apple -> apple.getColor().equals("紅色"));
appleList.forEach(System.out::println);
注意點:
- Predicate<Apple> applePerdicate 函數式接口
- apple -> apple.getColor().equals("紅色") lambda表達式
- appleList.forEach(System.out::println); 方法引用
appleList.forEach(apple -> System.out.println(apple));
函數式接口
只定義一個抽象方法的接口,可包含若干個默認方法
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms
*/
package java.util.function;
import java.util.Objects;
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Java 8中的常用函數式接口
==Lambda表達式可以被賦給一個變量,或傳遞給一個接受函數式接口作為參數的方法==
使用lambda
- 環(huán)繞執(zhí)行模式
資源處理(例如處理文件或數據庫)時一個常見的模式就是打開一個資源,做一些處理, 然后關閉資源。這個設置和清理階段總是很類似,并且會圍繞著執(zhí)行處理的那些重要代碼。
讀取文件
public String processFile(Function<BufferedReader,String> bufferedReaderStringFunction) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("E:\\aa.txt"));
} catch (IOException e) {
e.printStackTrace();
}
return bufferedReaderStringFunction.apply(br);
}
@Test
public void test3() {
//讀取一行
System.out.println(processFile(bufferedReader -> {
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}));
// 讀取所有行
System.out.println(processFile(bufferedReader -> bufferedReader.lines().collect(Collectors.joining(" "))));
}
- 實現runnable
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("傳統的匿名類方法實現");
}
}).start();
//無參數
new Thread(() -> {
System.out.println("lamda的方式實現");
System.out.println("lamda的方式實現");
}).start();
- 事件處理
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("傳統實現");
}
});
// Java 8方式:
show.addActionListener((e) -> {
System.out.println("lambda實現");
});
- 列表排序
// Java 8之前:
List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
//java 8 1
features.forEach(n -> System.out.println(n));
features.forEach(n -> {
if (n.equals("Lambdas")) {
System.out.println("過濾的值:" + n);
}
});
features.forEach(System.out::print);
//map
Map<String, Integer> items = new HashMap<>();
items.put("A", 10);
items.put("B", 20);
items.put("C", 30);
items.put("D", 40);
items.put("E", 50);
items.put("F", 60);
for (Map.Entry<String, Integer> entry : items.entrySet()) {
System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue());
}
items.forEach((k, v) -> System.out.println("map的key=" + k + " map的value=" + v));
items.forEach((k, v) -> {
System.out.println("map的key=" + k + " map的value=" + v);
if (k.equals("F")) {
System.out.println("過濾的map value值:" + v);
}
});
//list
List<String> items1 = new ArrayList<>();
items1.add("A");
items1.add("B");
items1.add("C");
items1.add("D");
items1.add("E");
//lambda
//Output : A,B,C,D,E
items1.forEach(item1 -> System.out.println(item1));
//Output : C
items1.forEach(item1 -> {
if ("C".equals(item1)) {
System.out.println(item1);
}
});
//method reference
//Output : A,B,C,D,E
items1.forEach(System.out::println);
//Stream and filter
//Output : B
items1.stream()
.filter(s -> s.contains("B"))
.forEach(System.out::println);
- 使用lambda表達式和函數式接口Predicate
public static void filter(List list,Predicate predicate){
list.forEach(n -> {
if(predicate.test(n)){//表達式是否滿足
System.out.print(n+" 1 ");
}
});
}
List<String> list = Arrays.asList("java", "scala", "c++", "haskhell", "lisp");
filter(list, str -> ((String)str).startsWith("j"));
filter(list, str -> ((String)str).length() > 4);
// 甚至可以用and()、or()和xor()邏輯函數來合并Predicate,
// 例如要找到所有以J開始,長度為四個字母的名字,你可以合并兩個Predicate并傳入
Predicate<String> startsWithJ = (n) -> n.startsWith("j");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
list.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach(n -> System.out.println("過濾"+n));
- Java 8中使用lambda表達式的Map和Reduce示例
// 不使用lambda表達式為每個訂單加上12%的稅
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
System.out.println("--------------");
//使用lambda表達式
costBeforeTax.stream().map(cost -> cost + .12*cost).forEach(System.out::println);
// 為每個訂單加上12%的稅
// 老方法:
List<Integer> costBeforeTax1 = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax1) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
//lambda
Double total1 = costBeforeTax1.stream().map(a -> a + .12*a).reduce((a,b) -> a+b).get();
System.out.println("Total1 :"+total1);
方法引用
方法引用讓你可以重復使用現有的方法定義,并像Lambda一樣傳遞它們。在一些情況下, 比起使用Lambda表達式,它們似乎更易讀,感覺也更自然。
例如
先前:
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
基本思想
==如果一個Lambda代表的只是“直接調用這個方法”,那最好還是用名稱 來調用它,而不是去描述如何調用它。事實上,方法引用就是讓你根據已有的方法實現來創(chuàng)建 Lambda表達式。但是,顯式地指明方法的名稱,你的代碼的可讀性會更好。==
使用
==目標引用放在分隔符::前,方法的名稱放在后面==
Lambda及其等效方法引用的例子
lambda | 等效的方法引用 |
---|---|
(Apple a) -> a.getWeight() | Apple::getWeight |
() -> Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) | String::substring |
(String s) -> System.out.println(s) | System.out::println |
構建方法引用的三種方式
- 指向靜態(tài)方法的方法引用(例如Integer的parseInt方法,寫作Integer::parseInt)
- 指向任意類型實例方法的方法引用(例如String 的 length 方法,寫作 String::length)。
其思想是:引用一個對象的方法,而這個對象本身是Lambda的一個參數。例如,Lambda 表達式(String s) -> s.toUppeCase()可以寫作String::toUpperCase。 - 指向現有對象的實例方法的方法引用(假設你有一個局部變量expensiveTransaction 用于存放Transaction類型的對象,它支持實例方法getValue,那么你就可以寫expensive- Transaction::getValue)。 其實說在Lambda中調用一個已經存在的外部對象中的方法。
字符串列表排序
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
//方法引用
str.sort(String::compareToIgnoreCase);
構造函數引用
對于一個現有構造函數,你可以利用它的名稱和關鍵字new來創(chuàng)建它的一個引用: ClassName::new。它的功能與指向靜態(tài)方法的引用類似。
例如,假設有一個構造函數沒有參數。 它適合Supplier的簽名() -> Apple。你可以這樣做:
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
//等價于
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
如果你的構造函數的簽名是Apple(Integer weight),那么它就適合Function接口的簽 名,于是你可以這樣寫:
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
//等價于
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
//一個由Integer構成的List中的每個元素都通過我們前面定義的類似的 map方法傳遞給了Apple的構造函數,得到了一個具有不同重量蘋果的List:
List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);
public static List<Apple> map(List<Integer> list, Function<Integer, Apple> f){
List<Apple> result = new ArrayList<>();
for(Integer e: list){
result.add(f.apply(e));
}
return result;
}
如果你有一個具有兩個參數的構造函數Apple(String color, Integer weight),那么 它就適合BiFunction接口的簽名,于是你可以這樣寫:
BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);
//等價于
BiFunction<String, Integer, Apple> c3 =(color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply("green", 110);
最終
類似于inventory.sort(comparing(Apple::getWeight));
,集合著行為參數化、匿名類、Lambda 表達式和方法引用等所有特點。