lambda表達式,又稱閉包(Closure)或稱匿名方法(anonymous method)。將Lambda表達式引入JAVA中的動機源於一個叫「行爲參數」的模式。這種模式可以解決需求變化帶來的問題,使代碼變得更加靈活。在JAVA8以前,參數模式十分囉嗦。Lambda表達式經過精簡的方式使用行爲模式克服了這個缺點php
函數式編程html
public class ScopeTest {
@Test
public void test_scope(){
Runnable runnable = () -> {
this.print();
};
runnable.run();
}
private void print(){
System.out.println("I can print");
}
}
複製代碼
final
labmda表達式使用外部的變量時,不可修改,默認定義成final java
函數式接口
只有一個抽象方法的接口咱們就稱之爲功能性接口,又簡稱 SAM 類型,即 Simple Abstract Method。會寫上註釋 @FunctionalInterface編程
//用這個註解來表示功能性接口
public interface Consumer<T> {
/** * Performs this operation on the given argument. * * @param t the input argument */
void accept(T t);
}
複製代碼
常見的內置函數以下:數組
java.lang.Runnable 執行動做服務器
java.util.function.Predicate 接收T對象並返回boolean閉包
java.util.function.Consumer 接收T對象,不返回值app
java.util.function.Function<T,R> 接收T對象,返回R對象.函數式編程
java.util.function.Supplier 提供T對象(例如工廠),不接收值函數
方法引用
方法引用有不少種,它們的語法以下:
靜態方法引用:ClassName::methodName
實例上的實例方法引用:instanceReference::methodName
超類上的實例方法引用:super::methodName
類型上的實例方法引用:ClassName::methodName
構造方法引用:Class::new
數組構造方法引用:TypeName[]::new
@Test
public void test_instance(){
Set<String> girls = new HashSet<>();
Set<String> names = new HashSet<>();
names.stream()
//實例::methodName
.filter(girls::contains)
.collect(Collectors.toList());
}
@Test
public void test_this(){
Set<String> names = new HashSet<>();
names.stream()
//this::methodName
.filter(this::hasAuth)
.collect(Collectors.toList());
}
private boolean hasAuth(String authKey){
return true;
}
複製代碼
Map<String,Person> map = new HashMap<>();
map.forEach((String name,Person person)->{
person.fly();
System.out.println(name+":"+person);
});
map.forEach((name,person)->{
// 無須判斷類型,自動根據上下文推斷
person.fly();
System.out.println(name+":"+person);
});
複製代碼
/** * 正常的代碼 */
@Test
public void test_person(){
CnResult<Person> result = null;
try {
// 只有這裏取值是不一樣的,其餘的處理是同樣的,包括try,catch,打日誌,定義異常等
Person entity = this.getPerson();
result = CnResult.success(entity);
}catch (CnException e){
logger.error(e.getMessage(),e);
result = CnResult.error(e.getErrorCode(),e.getMessage());
}
catch (Exception e) {
logger.error(e.getMessage(),e);
result = CnResult.error("1-1-1-1",e.getMessage());
}
Assert.assertNotNull(result);
}
@Test
public void test_animal(){
CnResult<Animal> result = null;
try {
// 只有這裏取值是不一樣的,其餘的處理是同樣的,包括try,catch,打日誌,定義異常等
Animal entity = this.getAnimal();
result = CnResult.success(entity);
}catch (CnException e){
logger.error(e.getMessage(),e);
result = CnResult.error(e.getErrorCode(),e.getMessage());
}
catch (Exception e) {
logger.error(e.getMessage(),e);
result = CnResult.error("1-1-1-1",e.getMessage());
}
Assert.assertNotNull(result);
}
/** * lambda代碼 */
@Test
public void test_lambda(){
//屏蔽全部細節,只把獲取對象的邏輯傳遞進去
CnResult<Person> person = WapperUtils.wapper(this::getPerson);
Assert.assertNotNull(person);
CnResult<Animal> animal = WapperUtils.wapper(this::getAnimal);
Assert.assertNotNull(animal);
}
public class WapperUtils {
private static final Logger logger = LoggerFactory.getLogger(WapperUtils.class);
/** * 包裹 * * @param supplier * @param <T> * @return */
public static <T> CnResult<T> wapper(Supplier<T> supplier){
try {
T entity = supplier.get();
CnResult<T> cnResult = CnResult.success(entity);
return cnResult;
}catch (CnException e){
logger.error(e.getMessage(),e);
return CnResult.error(e.getErrorCode(),e.getMessage());
}
catch (Exception e) {
logger.error(e.getMessage(),e);
return CnResult.error("1-1-1-1",e.getMessage());
}
}
}
複製代碼
只要是代碼片斷,找到變點化,抽象成lambda,把固定的代碼統一封裝,就能夠使用行爲的複用,減小代碼片斷和代碼包裹。
明確語義
@Test
public void test_first() {
List<Person> persons = new ArrayList<>();
persons.stream()
/** * 沒有明確的語義。須要根據過程計算推理得出結果,也不清楚背後的業務和場景 * */
.filter(person -> person.getAge() >= 18)
.collect(Collectors.toList());
}
@Test
public void test_second() {
List<Person> persons = new ArrayList<>();
persons.stream()
.filter(person -> {
/** * 若是職責變動,得修改代碼結構,並且代碼愈來愈難理解 */
if (person.getAge() >= 18) {
return true;
}
if (person.getName().startsWith("莊")) {
return true;
}
return false;
})
.collect(Collectors.toList());
}
@Test
public void test_third() {
List<Person> persons = new ArrayList<>();
persons.stream()
/** * 隨着業務變動須要不斷添加filter */
.filter(person -> person.getAge() >= 18)
.filter(person -> person.getName().startsWith("莊"))
.collect(Collectors.toList());
}
@Test
public void test_fourth() {
List<Person> persons = new ArrayList<>();
persons.stream()
/** * 隨着業務變動須要不斷添加filter */
.filter(Person::isAdult)
.filter(Person::belongToZhuang)
.collect(Collectors.toList());
}
@Test
public void test_final() {
List<Person> persons = new ArrayList<>();
/** * 有明確的語義,不用再面向細節加工一下,並且也方便後面的擴展 */
persons.stream()
.filter(Person::hasAuth)
.collect(Collectors.toList());
}
複製代碼
@Test
public void test_name_bad(){
List<Person> persons = new ArrayList<>();
/** * lambda一多的時候,沒有明確的參數,計算和理解起來很是吃力。 */
persons.stream()
.filter(e->e.getAge()>18)
.map(e->e.getName())
.filter(e->e.startsWith("莊"))
.collect(Collectors.toList());
}
@Test
public void test_name(){
List<Person> persons = new ArrayList<>();
persons.stream()
.filter(person->person.getAge()>18)
.map(person->person.getName())
.filter(name->name.startsWith("莊"))
.collect(Collectors.toList());
}
複製代碼
參數須要有明確語義
邏輯複雜
@Test
public void test_bad() {
List<Integer> values = new ArrayList<>();
int result = values.stream().mapToInt(e -> {
int sum = 0;
for (int i = 0; i < e; i++) {
if (e % i == 0) {
sum += i;
}
}
return sum;
}).sum();
System.out.println(result);
}
@Test
public void test_() {
List<Integer> values = new ArrayList<>();
int result = values.stream().mapToInt(this::toInt).sum();
System.out.println(result);
}
private Integer toInt(int e) {
int sum = 0;
for (int i = 0; i < e; i++) {
if (e % i == 0) {
sum += i;
}
}
return sum;
}
複製代碼
建議把多行lambda表達式主體轉移到一個命名函數中,而後使用方法引用
騰訊雲如今作活動,新手福利1000減750,雲服務器最低2折,1核2G內存50G硬盤3年最低只要600元!戳此瞭解詳情!
轉自:https://www.cnblogs.com/ansn001/p/9399360.html