之前剛學java的時候,直接用1.8的結果項目各種報錯,然後換了1.7果然也就沒有報錯,最近小道消息聽說jdk1.8有點穩定了,故下載了最新版的jdk1.8玩玩,官網下載的jdk實在慢的要死,下載了好久,想要下的快點的朋友可以到我的分享網盤這邊下載,是最新版的jdk-8u131版本——http://pan.baidu.com/s/1gfvb91h。
廢話不多說,回到主題。先看看一個Lambda表達式的小例子:
學過多線程的朋友都知道,我們創建可執行的線程對線總共有4種方式——繼承Thread類,實現Runnable接口,實現Callable接口以及使用線程池。
拿實現Runnable接口的實例對象來說,我們以前是這麼做的
Runnable r1 = new Runnable(){ //1
@Override //2
public void run(){ //3
System.out.println("匿名內部類"); //4
} //5
}; //6
沒錯,就是用匿名內部類去實現Runnable接口裏面的run抽象方法來創建一個Runnable的實例,用匿名內部類的好處當然有,比如對於不會重複使用的實例,它不需要特地建一個java類來實現這個接口。讓人不爽的地方也有:對於Runnable這種只有一個抽象方法的接口,我們很清楚的知道他要創建實例的話需要實現的是run這個抽象方法。因此,上面那段代碼真正有用的部分也就是註釋標識的1和4的部分。我們再看看用lambda表達式創建Runnable接口的實例:
Runnable r2 = () -> System.out.println("Lambda表達式");
Lambda表達式很簡略的就完成了這個過程,Lambda是一個匿名函數,我們可以把Lambda表達式理解爲一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。可以寫出更簡潔、更靈活的代碼。現在我們應該能稍微體會到這個特點了。而Lambda表達式主也要用於替換以前廣泛使用的內部匿名類,各種回調,比如事件響應器、傳入Thread類的Runnable等。
下面就來介紹介紹Lambda表達式:
一、基本語法
Lambda表達式在java語言中引入了一個新的語法元素和操作符,也就是"->",可以稱爲箭頭操作符或者Lambda操作符,該操作符將Lambda表達式拆分成兩部分:
左側 : 指定了Lambda表達式的參數列表
右側 : 指定了Lambda體,即Lambda表達式要執行的功能
// 語法格式一:無參數,無返回值
Runnable r1 = () -> System.out.println("Hello Lambda!");
// 語法格式二:有一個參數,並且無返回值
Consumer<String> fun = (x) -> System.out.println(x);
//語法格式三:若只有一個參數,小括號可以省略不寫
Consumer<String> fun = x -> System.out.println(x);
//語法格式四:有兩個以上的參數,有返回值,並且Lambda體中有多條語句
Comparator<Integer> com =(x,y) -> {
System.out.println("函數式接口");
return Integer.compare(x,y);
};
//語法格式五:若Lambda體中只有一條語句,return和大括號都可以省略不寫
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
//語法格式六:Lambda表達式的參數列表的數據類型可以省略不寫,因爲JVM編譯器通過上下文推斷出數據類型,即"數據推斷"
(Integer x,Integer y) -> Integer.compare(x,y);//x,y的數據類型Integer可以不寫
二、函數式接口
函數式接口指的是隻包含一個抽象方法的接口。你可以通過Lambda表達式來創建該接口的對象(若lambda表達式拋出一個受檢異常,那麼該異常需要在目標接口的抽象方法上進行聲明)可以在任意函數式接口上是用@FunctionalInterface註解來檢查它是否是一個函數式接口。
下面舉個函數式接口的例子(當然上面提到的Runnable接口也符合函數式接口):
@FunctionalInterface
public interface MyFunc(){
public int getValue(int num);
}
//當然你也可以在函數式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T>{
public T getValue(T t);
}
將Lambda表達式作爲參數傳遞:
//用上面定義的帶泛型的函數式接口當參數
public String toUpperString(MyFunc<String> mf,String str){
return mf.getValue(str);
}
String newString = toUpperString((str) -> str.toUpperCase(),"abcdef");
System.out.println(newString); //ABCDEF
java8中其實已經爲我們內置了很多函數式接口:包括四大函數式接口:消費型接口——Consumer<T>、供給型接口——Supplier<T>、函數型接口——Function<T,R>以及斷言型Predicate<T>,其他接口爲這四個接口的子接口,具體細節如下圖
拿四大內置函數式接口舉個例子:
//Consumer<T> 消費型接口:
@Test
public void test1(){
consume(100, (m) -> System.out.println("嫖一次花費:"+m));
}
public void consume(double money, Consumer<Double> con){
con.accept(money);
}
//Supplier<T> 供給型接口:
@Test
public void test2(){
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
for (Integer num : numList){
System.out.println(num);
}
}
//產生一些整數,並放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i = 0;i < num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
//Function<T,R> 函數型接口:
@Test
public void test3(){
String newStr = strHandler(" 我日 ",(str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("我大skt威武",(str) -> str.substring(1,3));
System.out.println(subStr);
}
//需求:用於處理字符串
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
//Predicate<T> 斷言型接口:
@Test
public void test4(){
List<String> list = Arrays.asList("Hello","atguigu","Lambda","www","ok");
List<String> strList = filterStr(list,(str) -> str.length() > 3);
for(String str : strList){
System.out.println(str);
}
}
//將滿足條件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for(String str : list){
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
三、方法引用、構造器引用、數組引用(使用"::"操作符)
①方法引用 :
若Lambda體中的內容有方法已經實現了,我們可以使用"方法引用".
方法引用主要三種使用情況:1、對象::實例方法
2、 類::靜態方法
3、 類::實例方法
舉個例子:
//對象::實例方法名
@Test
public void test1(){
Consumer<String> con1 = (x) -> System.out.println(x);
Consumer<String> con2 = System.out::println;//對象::實例方法名
con2.accept("abcdef");
}
@Test
public void test2(){
Employee emp = new Employee();//Employee爲一個javaBean,成員變量有String類型的name以及Integer類型的age,以及對應的getter、setter方法
Supplier<String> sup1 = () -> emp.getName();
String str = sup1.get();
System.out.println(str);
Supplier<Integer> sup2 = emp::getAge;//對象::實例方法名
Integer num = sup2.get();
System.out.println(num);
}
//類::靜態方法名
@Test
public void test3(){
Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com2 = Integer::compare;//類::靜態方法名
System.out.println(com2.compare(1,2));
}
//類::實例方法名
@Test
public void test4(){
BiPredicate<String,String> bp1 = (x,y) -> x.equals(y);
BiPredicate<String,String> bp2 = String::equals;//類::實例方法名
}
2、若Lambda參數列表中的第一參數是實例方法的調用者,而第二參數是實例方法的參數,可以使用類::實例方法名
②構造器引用
格式: ClassName::new
老規矩,例子:
//構造器引用
@Test
public void test5(){
Supplier<Employee> sup = () -> new Employee();
//構造器引用方式
Supplier<Employee> sup2 = Employee::new;//無參構造
Employee emp = sup2.get();
System.out.println(emp);
}
@Test
public void test6(){
Function<String,Employee> fun = (x) ->new Employee(x);
Function<String,Employee> fun2 = Employee::new;//一個參數的構造函數
System.out.println(fun2.apply("xxName"));
BiFunction<String,Integer,Employee> fun3 = Employee::new;//兩個參數的構造函數
System.out.println(fun3.apply("xxName",11));
}
注意:需要調用的構造器的參數列表要與函數式接口中抽象方法的參數列表保持一致
③數組引用
格式: Type[]::new
例子:
//數組引用
@Test
public void test7(){
Function<Integer,String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer,String[]> fun2 = String[]::new;
String[] str2 = fun2.apply(20);
System.out.println(str2.length);
}