概要
Java Lambda表達式是一種匿名函數,他是沒有聲明的方法,即沒有訪問修飾符,返回值聲明和名字
作用
1. 傳遞行爲,而不僅僅是值
2. 提升抽象層次
3. API重用性更好
4. 更加靈活
Lambda結構
- 一個Lambda表達式可以有零個或多個參數
- 參數的類型既可以明確聲明,也可以根據上下文來推斷。例如:(int a)與(a)效果相同
- 所有參數需包含在圓括號內,參數之間用逗號相隔。例如:(a,b)或者(int a,int b)或(String a,String b,float c)
- 空圓括號代表參數集爲空。例如:()-> 42
Lambda 箭頭左側(參數)->箭頭右側(執行體) 當左側沒有參數時 左側括號是不能省略的
public class Test1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 3, 8);
/*
1. @FunctionalInterface 凡是一個類加上該註解 都是函數式接口
請注意,加了改接口可以使用
lambda表達式、方法引用或構造函數引用。
2.滿足以下規則 不然系統會給你生成一個錯誤信息
1.類型是接口類型,而不是批註類型、枚舉或類
2.滿足函數式接口要求
*/
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
//我們可以將行爲理解爲對應函數式接口方法的實現方法
System.out.println(integer);
}
});
}
}
關於函數式接口:
1.如果一個接口只有一個抽象方法,那麼改接口就是一個函數式接口
2.如果我們在某個接口聲明@FunctionalInterface註解,那麼編譯就會暗中函數式接口定義來要求該接口
3.如果某個接口只有一個抽象方法,但我們並沒有該接口聲明@FunctionalInterface註解。那麼編譯器依舊會將該接口看做函數式接口。
4.注意,函數接口的實例可以用lambda表達式、方法引用或構造函數引用創建
Iterable
jdk1.5 ,實現該接口的類可以進行迭代 ,1.8以後集合都可以直接使用默認方法forEach方法進行迭代
(jdk8之後 接口可以寫默認方法和 static實現方法)
forEach
doc文檔
對Iterable的每個行爲執行給定的操作,直到處理完所有行爲或操作引發異常爲止。除非實現類另有指定,否則按迭代順序(如果指定了迭代順序)執行操作。操作引發的異常將取決於調用方
forEach改方法是將==行爲動作(函數 在java中一種特別的對象)==當做參數進行傳遞 在jdk1.8之前方法參數只能傳遞具體數據
實例1
public class Test1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 3, 8);
/*
1. @FunctionalInterface 凡是一個類加上該註解 都是函數式接口
請注意,加了改接口可以使用
lambda表達式、方法引用或構造函數引用。
2.滿足以下規則 不然系統會給你生成一個錯誤信息
1.類型是接口類型,而不是批註類型、枚舉或類
2.滿足函數式接口要求
*/
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
list.forEach(x-> System.out.println(x));
Consumer<Integer> s = x-> System.out.println(x);
}
}
Consumer(重要)
doc文檔
代表着一個接受單個輸入參數且不返回結果的操作。與大多數其他函數式接口不同,消費者希望通過副作用(就是可能會去修改接收單個參數)進行操作。類似Test1中accept執行給定行爲具體操作給定的行爲參數即可
當調用該接口可以直接使用Lambda表達式操作 如accept方法 ( T x)->方法體
我們可以對流中元素的屬性進行修改
Student student = new Student();
List<Student> integers = Arrays.asList(student);
List<Student> students = integers.stream().peek(x -> {
x.setScore(90);
x.setUsername("zhangsan");
}).collect(Collectors.toList());
System.out.println(students);
}
項目中如何使用?
實例2
@FunctionalInterface
interface MyIterfece {
void test();
// void test2(String s);
/*
爲什麼函數式接口裏有兩個抽象方法 系統不報錯
因爲 該接口的實現類 一定繼承於Object基類 基類存在toString()方法 無需實現類進行創建 所以函數式接口允許存在
Object基類的抽象方法
*/
@Override
String toString();
}
class Test2 {
public void meTest(MyIterfece myIterfece) {
System.out.println("1");
myIterfece.test();
System.out.println("2");
}
public static void main(String[] args) {
Test2 test2 = new Test2();
test2.meTest(new MyIterfece() {
@Override
public void test() {
System.out.println("mytest");
}
});
/*
當函數式接口裏的方法沒有參數時 ()不能省略
因爲函數式接口只有一個抽象方法 所以方法名就不那麼重要
*/
test2.meTest(() -> System.out.println("mytest"));
System.out.println("====================================");
//等同於MyIterfece接口實現類
MyIterfece myIterfece = () -> System.out.println("mytest");
System.out.println(myIterfece.getClass());
System.out.println(myIterfece.getClass().getSuperclass());
//打印該實現類實現的接口
System.out.println(myIterfece.getClass().getInterfaces()[0]);
}
}
打印結果
/*
1
mytest
2
1
mytest
2
====================================
MyIterfece接口的實現類
class com.shengsiyuan.jdk8.Test2$$Lambda$2/1603195447
實現類接口的父類
class java.lang.Object
實現類實現的接口
interface com.shengsiyuan.jdk8.MyIterfece
*/
傳統外部迭代:通過下標一個一個迭代出數據(for循環 增強for 迭代器)
內部迭代: 從內部以一個取出數據
例3:將集合英文變成大寫
public class Test3 {
public static void main(String[] args) {
// /*
// 對於lambda表達式來說 完全不用管函數式接口裏方法名是什麼,因爲其實通過上下文進行類型推斷
// 只需要關係返回值和參數即可
// */
// TheInterface theInterface = () -> {
// };
// System.out.println(theInterface.getClass().getInterfaces()[0]);
//
// TheInterface2 theInterface2 = () -> {
// };
// System.out.println(theInterface2.getClass().getInterfaces()[0]);
//
// //lambda表達式必須依附上下文定位到對應類型 如果沒有上下文就會報錯
//// ()->{};
//
//
// new Thread(() -> System.out.println("thread")).start();
//將集合英文變成大寫
List<String> list = Arrays.asList("hello", "world", "hello world");
// List<String> list2 = Lists.newArrayList();
// list.forEach(x -> list2.add(x.toUpperCase()));
// list2.forEach(x -> System.out.println(x));
/*
採用stream流的方式來編寫
map 映射的意思就是將一個值映射爲另外一個值
Stream方法中也有個forEach方法作用類似Iterable接口的forEach
*/
// list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));
/*
採用方法引用的方式
x -> x.toUpperCase() 等價於 String::toUpperCase
*/
list.stream().map(String::toUpperCase).forEach(System.out::println);
/*
Function<String,String>
輸入是調用toString lambda對象的第一個參數 x(也就是String對象) ( x -> x.toUpperCase() 等價於 String::toUpperCase)
輸出是toString方法返回的值
*/
Function<String, String> function = String::toString;
System.out.println(function.getClass().getInterfaces()[0]);
}
@FunctionalInterface
interface TheInterface {
void myMethod();
}
@FunctionalInterface
interface TheInterface2 {
void myMethod2();
}
}
例4
public class StringComparator {
public static void main(String[] args) {
/*
集合倒敘排序
*/
List<String> list = Arrays.asList("zhangsan", "zhaojie", "maliu");
Collections.sort(list, (x, y) -> x.compareTo(y));
System.out.println(list);
}
}
在例三中stream().map(Function<? super T, ? extends R> mapper)
Function(重要)
Function<String,String>
輸入:是調用toString lambda對象的第一個參數 x(也就是String對象) ( x -> x.toUpperCase() 等價於 String::toUpperCase)
返回:是toString方法返回的值
*/
Function<String, String> function = String::toString;
System.out.println(function.getClass().getInterfaces()[0]);
//map參數就是Function<T,R> 此時 將x當做參數,x.toUpperCase() 當做值返回
list.stream().map(x -> x.toUpperCase()).forEach(y -> System.out.println(y));
例5
public class FunctionTest {
public static void main(String[] args) {
FunctionTest functionTest = new FunctionTest();
System.out.println(functionTest.function(5, x -> x * 2));
/*
x -> x +2 是一個行爲 當調用方法時他將 x +2 這種行爲動作傳過去
執行到function.apply(a)時 此時纔會執行改行爲 執行完返回對應數據
此時行爲是用的時候傳過去對應的行爲(加減乘除)
*/
System.out.println(functionTest.function(10, x -> x + 2));
System.out.println(functionTest.function(10, x -> x * x));
System.out.println(functionTest.function2(10, x -> x + "你好啊!"));
//傳統方式
System.out.println(functionTest.method(2));
}
//接受一個參數和一個行爲
public int function(int a, Function<Integer, Integer> function) {
Integer apply = function.apply(a);
return apply;
}
public String function2(int a, Function<Integer, String> function) {
String apply = function.apply(a);
return apply;
}
/*
傳統方法,我們需要提前將行爲定義好 再去調用對應的方法
在運行之前行爲都是定義好的做不到靈活多變
*/
public int method(Integer a) {
return a * 5;
}
public int method2(Integer a) {
return a + 2;
}
public int method3(Integer a) {
return a * a;
}
}
10
12
10你好啊!
Function中兩個默認方法 一個靜態方法
/*
總是返回始終返回調用方
其輸入參數的函數
*/
static <T> Function<T, T> identity() {
return t -> t;
}
/*
組合函數 將傳過來的Function行爲apply執行結果 當做第二個Function的行爲的執行參數
返回apply結果
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));//返回結果
}
/**
組合函數 與上面相反 是將當前的Function行爲apply執行結果 當做傳過來Function的行爲的執行參數
返回apply結果
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
//t = 當前Function調用andThen的對象行爲
return (T t) -> after.apply(apply(t));
}
例6
Function<String, String> function3 = y -> y + "1";
Function<String, String> function2 = x -> x + "nihao";
//將String對象 傳過去
Function<String, String> andThen = function2.andThen(function3);
Function<String, String> compose = function2.compose(function3);
//返回結果根據用戶傳過來的的行爲決定 ,參數再去執行對應的行爲
String andThenapply = andThen.apply("22");
System.out.println(andThenapply);
System.out.println("=======================================");
String composeapply = compose.apply("22");
System.out.println(composeapply);
打印結果
22nihao1
=======================================
221nihao//先執行y -> y + "1".apply("22") 得到的結果221 在執行 x -> x + "nihao".apply(221)