Java8新特性lambda表示式快速入門
序
隨著Java語言的不斷髮展,Java8提供的新特性lambda表示式也成為越來越多開發者喜歡的寫法,為了順應時代的變化,需要好好地學習這種寫法,並應用在平時的開發中。我們先從簡單的例子著手,一步步的深入到lambda的複雜寫法,首先我們的任務是建立一個用來比較兩個int值大小的介面,並給它新增具體的實現方法,然後進行呼叫
interface Comparator{
int compare(int a,int b);
}
常規寫法
寫法一:新建類並實現該介面
建立MyComparator.java並實現Comparator介面:
class MyComparator implements Comparator{
@Override
public int compare(int a, int b) {
return a > b ? a : b; //輸出最大的值
}
}
然後進行呼叫:
public static void main(String[] args) { Comparator comparator = new MyComparator(); int res = comparator.compare(1, 10); System.out.println(res); }
寫法二:使用匿名函式
直接new介面
public static void main(String[] args) { //使用匿名方法實現 Comparator comparator = new Comparator() { @Override public int compare(int a, int b) { return a > b ? a : b; } }; int res = comparator.compare(1, 10); System.out.println(res); }
lambda寫法
跟上面的常規寫法不同的是,lambda只關注方法的入參和方法體中具體實現的邏輯,其餘能省的程式碼全部都可以省略,我們先看一下第一種寫法:
寫法一:lambda常規寫法
自己比較一下跟匿名函式寫法的差別,是不是非常的簡潔,lambda只聲明瞭入參和方法體內的邏輯,關鍵地方在於入參和方法體之間使用了->
箭頭符號進行連線,箭頭符號讀作gose to
。
public static void main(String[] args) {
//簡單的lambda寫法
Comparator comparator = (int a,int b) -> {
return a > b ? a : b;
};
int res = comparator.compare(1, 10);
System.out.println(res);
}
到這裡你應該有一個基本的印象了,這一步轉變非常重要,停下來好好思考一下。
寫法二:lambda簡寫
仔細觀察可以發現寫法一中仍然有多餘的程式碼,比如說我們在之前的介面中已經宣告過一次入參的型別,在使用的時候又聲明瞭一次,這顯然是多餘的寫法。其實上面的寫法依然可以更簡潔,只要滿足下面這幾點,都可以省略。
- 當入參型別和初始定義型別一致時,可以省略入參型別。
- 當方法體中只有一句程式碼時,可以省略大括號。
- 當方法體中只有一句程式碼,並且需要返回值時,可以省略
return
關鍵字。
更簡單的寫法如下:
public static void main(String[] args) {
//省略了入參型別、大括號、return關鍵字
Comparator comparator = (a,b) -> a > b ? a : b;
int res = comparator.compare(1, 10);
System.out.println(res);
}
中場疑問
上面我們定義的介面只有一個抽象方法compare()
,有的同學就會有疑問,那如果有多個抽象方法怎麼寫呢?問的很好!由於lambda的寫法非常的簡潔,但這是在一定的條件限制下才能這麼寫,其中非常重要的一點就是,只有一個抽象方法的接口才支援lambda的寫法,這種介面被稱為函式式介面,可以通過@FunctionalInterface
註解進行修飾。
@FunctionalInterface只修飾函式式介面,即只有一個抽象方法的介面。
lambda的方法引用
寫法一
lambda表示式中方法體只有一行程式碼才能有最簡單的寫法,那如果我想寫一些複雜的業務就不能簡寫了嗎?其實並不是的,這時候我們就可以泳道lambda的方法引用,將較複雜的業務封裝成方法,然後在lambda中進行呼叫,假如還是上面的例子,我們可以將方法體中的那一行程式碼封裝成一個方法:
public static int max(int a,int b){
//這裡可以寫一些較為複雜的業務
return a > b ? a : b;
}
這時候lambda表示式就可以這麼寫:
public static void main(String[] args) {
//方法體中引用max函式
Comparator comparator = (a,b) -> max(a,b);
int res = comparator.compare(1, 10);
System.out.println(res);
}
上面就是一個普通的lambda方法引用,簡單的說就是將方法體中的程式碼封裝成一個一方法,然後在呼叫這個方法即可,需要注意的是max
函式中的返回值必須要跟介面中的抽象方法一致,否則就會報異常。
寫法二
上面的寫法雖然簡單有效,但依然有多餘的地方,那就是方法引數重複了,程式碼(a,b) -> max(a,b);
中出現了兩次(a,b)
,在實際開發中,我們一般採用另一種寫法來代替->
箭頭符號,請大家牢記語法:
方法的隸屬者::方法名。
隸屬者可以理解為,能夠呼叫這個方法的物件,如果該方法是static
修飾的,則隸屬者位當前類;如果不是static
修飾的,則隸屬者屬於一個物件。如果聽不懂也沒關係,看一個栗子就清晰明瞭。
首先看一下完整的程式碼:
//定義介面
interface Comparator {
int compare(int a, int b);
}
public class Program {
public static void main(String[] args) {
//使用lambda的方法引用呼叫max方法,隸屬者為類
Comparator comparator = Program::max;
int res = comparator.compare(1, 10);
System.out.println(res);
}
//用static修飾的方法
public static int max(int a,int b){
return a > b ? a : b;
}
}
從上面程式碼中我們發現,將之前的(a,b) -> max(a,b);
寫成了Program::max;
,因為方法max
被關鍵字static
修飾了,因此隸屬者應該是Program
這個類。也可以發現省略了引數(a,b)
,這麼寫就已經是非常的簡潔了,但這麼寫需要滿足兩個基本條件:
- 只有介面中抽象方法的引數格式跟定義的方法引數格式一樣,才能省略引數。
- 介面中抽象方法的返回值必須跟定義的方法的返回值一樣。
這就是java8引入的新特性,一切為了簡潔明瞭。
方法不是static怎麼寫?
理解了上面,這個就比較好懂了,貼一下程式碼,不做解釋了
public class Program {
public static void main(String[] args) {
//建立一個Program物件
Program p = new Program();
//使用lambda的方法引用呼叫max方法,隸屬者為物件
Comparator comparator = p::max;
int res = comparator.compare(1, 10);
System.out.println(res);
}
//沒有被static修飾的方法
public int max(int a,int b){
return a > b ? a : b;
}
}
練習題
素材如下:
Person.java
class Person{
String name ;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
準備集合
public class Program {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("張三",18));
list.add(new Person("李四",30));
list.add(new Person("王五",16));
list.add(new Person("趙六",20));
list.add(new Person("田七",40));
list.add(new Person("馬八",25));
printList(list);
}
//列印集合
public static void printList(List<Person> list){
for (Person person : list) {
System.out.println(person);
}
}
}
1. 將集合按照指定屬性進行排序
按照person的年齡進行從低到高排序
普通的排序方式:
//使用正常的方式排序
list.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
問題:你能將普通的排序方式改造成lambda的方式嗎?寫出來後思考還有其他寫法嗎?
2. 將list.forEach
方法改造成lambda方式
正常的list遍歷:
list.forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.toString());
}
});
問題:如何改造成lambda的方式??改造之後修改練習1中的遍歷程式碼
3.刪除集合中指定元素
刪除年齡大於35的人
正常的刪除:
list.removeIf(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.age > 35;
}
});
問題:如何改造成lambda的方式??
答案
- 排序(二選一)
list.sort((p1,p2) -> p1.getAge() - p2.getAge());
Collections.sort(list,(p1,p2) -> p1.getAge() - p2.getAge());
- 遍歷
list.forEach(person -> System.out.println(person.toString()));
- 刪除指定元素
list.removeIf(person -> person.age > 35);