一、為什么使用Lambda表達(dá)式?
Lambda是一個(gè)匿名函數(shù)吞歼,我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)∷可以寫出更簡(jiǎn)潔篙骡、更靈活的代碼。作為一種更緊湊的代碼風(fēng)格丈甸,使Java的語言表達(dá)能力得到了提升糯俗。
二、Lambda表達(dá)式初識(shí)
下面我們來看一下幾個(gè)Lambda表達(dá)式的例子:
-
從匿名類到Lambda的轉(zhuǎn)換
Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello World!" ); } };
-
Lambda表達(dá)式
Runnable r1 = () -> System.out.println("Hello Lambda!");
-
原來使用匿名內(nèi)部類作為參數(shù)傳遞
TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>(){ @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } });
Lambda表達(dá)式作為參數(shù)傳遞
-
-
TreeSet<String> ts2=new TreeSet<>(
(o1,o2) -> Integer.compare(o1.lenrth(),o2.length())
);
三睦擂、Lambda表達(dá)式引入點(diǎn)
要求:現(xiàn)在有一個(gè)員工集合得湘,要求按照員工的薪資、員工的年齡得到符合要求的員工集合顿仇。
首先淘正,我們定義一個(gè)員工類,Employee.java
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
第一種解決方案:也是我們比較常規(guī)思維臼闻,就是定義一個(gè)方法鸿吆,對(duì)應(yīng)要求得到指定的員工集合,這里我們就只看“獲取公司中工資大于 5000 的員工信息”定義的方法如下:
public List<Employee> filterEmployeeSalary(List<Employee> emps){
List<Employee> list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getSalary() >= 5000){
list.add(emp);
}
}
return list;
}
然后每一種要求對(duì)應(yīng)一種方法述呐,但是這種會(huì)很冗余惩淳,本來就一個(gè)條件不同,但是要寫這么多相同的代碼乓搬。這樣思犁,我們就要來優(yōu)化了,我們更好的思維就是通過設(shè)計(jì)模式來操作了缤谎。
第二種解決方案:利用設(shè)計(jì)模式中的策略模式來優(yōu)化抒倚。
首先,我們定義一個(gè)接口
public interface MyPredicate<T> {
public boolean test(T t);
}
然后只要有一個(gè)要求坷澡,我就定義一個(gè)對(duì)應(yīng)的類實(shí)現(xiàn)這個(gè)接口托呕,這里依然是使用上述的要求:獲取公司中工資大于 5000 的員工信息。
public class FilterEmployeeForSalary implements MyPredicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getSalary() >= 5000;
}
}
然后定義一個(gè)方法频敛。如下
public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
List<Employee> list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
最后在測(cè)試代碼里面匿名內(nèi)部類项郊。
@Test
public void test5(){
List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getId() <= 103;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
}
這樣就能得到要求的員工集合了。
但是這樣看來斟赚,還是有點(diǎn)冗余着降,因?yàn)槊恳粋€(gè)要求我都要?jiǎng)?chuàng)建一個(gè)類。所以拗军,Lambda表達(dá)式就引入了任洞。
第三種方案:引進(jìn)Lambda表達(dá)式
@Test
public void test6(){
List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
list.forEach(System.out::println);
System.out.println("------------------------------------------");
List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
list2.forEach(System.out::println);
}
四蓄喇、Lambda表達(dá)式語法
Lambda表達(dá)式在Java語言中引入了一個(gè)新的語法元素和操作符。這個(gè)操作符為“->”,該操作符被稱為L(zhǎng)ambda操作符或箭頭操作符交掏。它將Lambda分為兩個(gè)部分:
左側(cè):指定了Lambda表達(dá)式需要的所有參數(shù)妆偏。
右側(cè):指定了Lambda體,即Lambda表達(dá)式要執(zhí)行的功能盅弛。
1钱骂、語法格式一:無參數(shù),無返回值
() -> System.out.println("Hello Lambda!");
示例代碼:
@Test
public void test1(){
int num = 0;//jdk 1.7 前挪鹏,必須是 final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
//無參無返回值
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
2见秽、語法格式二:有一個(gè)參數(shù),并且無返回值
(x) -> System.out.println(x)
示例代碼:
@Test
public void test2(){
Consumer<String> con = (x) -> System.out.println(x);
con.accept("你是大傻子讨盒!");
}
3解取、語法格式三:若只有一個(gè)參數(shù),小括號(hào)可以省略不寫
x -> System.out.println(x)
示例代碼:
@Test
public void test2(){
Consumer<String> con = x -> System.out.println(x);
con.accept("你是大傻子催植!");
}
4肮蛹、語法格式四:有兩個(gè)以上的參數(shù)勺择,有返回值创南,并且 Lambda 體中有多條語句
Comparator<Integer> com = (x, y) -> {
System.out.println("函數(shù)式接口");
return Integer.compare(x, y);
};
示例代碼:
@Test
public void test3(){
Comparator<Integer> com = (x, y) -> {
System.out.println("函數(shù)式接口");
return Integer.compare(x, y);
};
}
5、語法格式五:若 Lambda 體中只有一條語句省核, return 和 大括號(hào)都可以省略不寫
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
示例代碼:
@Test
public void test4(){
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
}
6稿辙、 語法格式六:Lambda 表達(dá)式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,因?yàn)镴VM編譯器通過上下文推斷出气忠,數(shù)據(jù)類型邻储,即“類型推斷”
(Integer x, Integer y) -> Integer.compare(x, y);
示例代碼:
@Test
public void test5(){
// String[] strs;
// strs = {"aaa", "bbb", "ccc"};
//字符串?dāng)?shù)組定義時(shí)不能分兩步寫,這樣寫是因?yàn)轭愋屯茢嗑稍搿K跃幾g才能通過的吨娜。
List<String> list = new ArrayList<>();
// ArrayList<>的類型可以不用寫,這樣也是因?yàn)轭愋屯茢嗟摹? show(new HashMap<>());
}
public void show(Map<String, Integer> map){
}
語法小總結(jié):
上聯(lián):左右遇一括號(hào)省
下聯(lián):左側(cè)推斷類型省
橫批:能省則省
五淘钟、類型推斷(比較重要)
上述的語法六宦赠,Lambda表達(dá)式中的參數(shù)類型都是由編譯器推斷得出的。Lambda表達(dá)式中無需指定類型米母,程序依然可以編譯勾扭,這是因?yàn)閖avac根據(jù)程序的上下文,在后臺(tái)推斷出了參數(shù)的類型铁瞒。Lambda表達(dá)式的類型依賴于上下文環(huán)境妙色,是由編譯器推斷出來的。這就是所謂的“類型推斷”慧耍。
六身辨、函數(shù)式接口
這里就稍微講一下丐谋,下面的文章會(huì)詳細(xì)講解
1、什么是函數(shù)式接口煌珊?
(1)笋鄙、只包含一個(gè)抽象方法的接口,稱為函數(shù)式接口怪瓶。
(2)萧落、你可以通過Lambda表達(dá)式來創(chuàng)建該接口的對(duì)象。(若Lambda表達(dá)式拋出一個(gè)受檢異常洗贰,那么該異常需要在目標(biāo)接口的抽象方法上進(jìn)行聲明)找岖。
(3)、我們可以在任意函數(shù)式接口上使用@FunctionalInterface注解敛滋,這樣做可以檢查它是否是一個(gè)函數(shù)式接口许布,同時(shí)javadoc也會(huì)包含一條聲明,說明這個(gè)接口是一個(gè)函數(shù)式接口绎晃。
2蜜唾、自定義函數(shù)式接口
示例代碼:
@FunctionalInterface
public interface MyFun {
public Integer getValue(Integer num);
}
七、Lambda練習(xí)
1庶艾、調(diào)用Collecions.sort()方法袁余,通過定制排序比較兩個(gè)Employee(先按年齡比,年齡相同按姓名比)咱揍,使用Lambda作為參數(shù)傳遞颖榜。
步驟一:先創(chuàng)建一個(gè)pojo
package com.nieshenkuan.pojo;
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String show() {
return "測(cè)試方法引用!";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
步驟二:事先聲明一個(gè)emps員工集合
List<Employee> emps = Arrays.asList(
new Employee(101, "張三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "趙六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
步驟三:測(cè)試實(shí)現(xiàn)要求
@Test
public void test1(){
Collections.sort(emps, (e1, e2) -> {
if(e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else{
return -Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
2煤裙、(1)掩完、聲明函數(shù)式接口,接口中聲明抽象方法硼砰,public String getValue(String str);
(2)且蓬、聲明類TestLambda,類中編寫方法使用接口作為參數(shù)题翰,將一個(gè)字符串轉(zhuǎn)換成大寫恶阴,并作為方法的返回值。
(3)遍愿、再將一個(gè)字符串的第2個(gè)和第4個(gè)索引位置進(jìn)行截取子串存淫。
步驟一:先聲明一個(gè)函數(shù)式接口MyFunction,并接口中聲明抽象方法沼填,public String getValue(String str);
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
步驟二:編寫一個(gè)方法處理字符串,這個(gè)方法在Lambda類中定義的桅咆。
//需求:用于處理字符串
public String strHandler(String str, MyFunction mf){
return mf.getValue(str);
}
步驟三:測(cè)試,實(shí)現(xiàn)具體的要求
@Test
public void test2(){
//這個(gè)是測(cè)試去除字符串的前后空格
String trimStr = strHandler("\t\t\t 你是大傻逼 ", (str) -> str.trim());
System.out.println(trimStr);
//這個(gè)是測(cè)試將字符串轉(zhuǎn)換成大寫的
String upper = strHandler("abcdef", (str) -> str.toUpperCase());
System.out.println(upper);
//截取字符串的指定索引的字符串
String newStr = strHandler("我大望江縣威武", (str) -> str.substring(2, 5));
System.out.println(newStr);
}
3坞笙、(1)岩饼、聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口荚虚,泛型類型為<T,R>T為參數(shù),R為返回值籍茧。
(2)版述、接口中聲明對(duì)應(yīng)抽象方法
(3)、在TestLambda類中聲明方法寞冯,使用接口作為參數(shù)渴析,計(jì)算兩個(gè)long型參數(shù)的和。
(4)吮龄、在計(jì)算兩個(gè)long型參數(shù)的乘積俭茧。
步驟一:聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口,泛型類型為<T,R>T為參數(shù)漓帚,R為返回值
public interface MyFunction2<T, R> {
public R getValue(T t1, T t2);
}
步驟二:編寫一個(gè)方法母债,計(jì)算兩個(gè)long型的數(shù)據(jù)
//需求:對(duì)于兩個(gè) Long 型數(shù)據(jù)進(jìn)行處理
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
System.out.println(mf.getValue(l1, l2));
}
步驟三:測(cè)試,并實(shí)現(xiàn)要求尝抖。
@Test
public void test3(){
//實(shí)現(xiàn)兩個(gè)long型的數(shù)值的和
op(100L, 200L, (x, y) -> x + y);
//實(shí)現(xiàn)兩個(gè)long型的數(shù)值的積
op(100L, 200L, (x, y) -> x * y);
}