Lambda
Lambda是一個(gè)匿名函數(shù)宰闰,我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣傳遞)衔瓮。可以寫出更簡(jiǎn)潔槐沼、更靈活的代碼。作為一種更緊湊的代碼風(fēng)格,使Java的語(yǔ)言表達(dá)能力得到提升
體驗(yàn)
/**
* lambda 初體驗(yàn)
*/
public class lambdaTest {
@Test
public void test() {
// 匿名內(nèi)部類
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(comparator);
// lambda
Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);
TreeSet<Integer> treeSet1 = new TreeSet<>(comparator1);
}
}
演變
原始做法
有這么一個(gè)需求母赵,求一個(gè)班級(jí)中年齡大于16歲的同學(xué)逸爵;
public class Student {
private String name;
private Integer age;
private Integer scope;
public Student() {
}
public Student(String name, Integer age, Integer scope) {
this.name = name;
this.age = age;
this.scope = scope;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getScope() {
return scope;
}
public void setScope(Integer scope) {
this.scope = scope;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", scope=" + scope +
'}';
}
}
List<Student> students = Arrays.asList(
new Student("張三", 18, 90),
new Student("李四", 14, 95),
new Student("王五", 16, 98),
new Student("牛二", 18, 95),
new Student("趙六", 15, 96)
);
// 年齡大于16
@Test
public void test01() {
List<Student> stus = new ArrayList<>();
for (Student student : students) {
if (student.getAge() > 16) {
stus.add(student);
}
}
for (Student student : stus) {
System.out.println(student);
}
}
需求變更:求班級(jí)中分?jǐn)?shù)大于95的同學(xué)
// 分?jǐn)?shù)大于95
@Test
public void test02() {
List<Student> stus = new ArrayList<>();
for (Student student : students) {
if (student.getScope() > 95) {
stus.add(student);
}
}
for (Student student : stus) {
System.out.println(student);
}
}
可以看出,有效代碼只有if
判斷中的條件凹嘲,這樣情況下师倔,隨著需求的變更,我們會(huì)書寫很多重復(fù)性代碼
優(yōu)化方式一:策略設(shè)計(jì)模式
需求:年齡大于16
// 定義過濾接口
public interface MyPredicate<T> {
public boolean test(T t);
}
// 年齡過濾
public class AgePredicate implements MyPredicate<Student> {
@Override
public boolean test(Student student) {
return student.getAge() > 16;
}
}
public List<Student> filterStu(List<Student> students, MyPredicate myPredicate) {
List<Student> list = new ArrayList<>();
for (Student student : students) {
if (myPredicate.test(student)) {
list.add(student);
}
}
return list;
}
// 年齡大于16
@Test
public void test03() {
List<Student> students = filterStu(this.students, new AgePredicate());
for (Student student : students) {
System.out.println(student);
}
}
需求變更:分?jǐn)?shù)大于95
// 分?jǐn)?shù)過濾
public class ScopePredicate implements MyPredicate<Student> {
@Override
public boolean test(Student student) {
return student.getScope() > 95;
}
}
public List<Student> filterStu(List<Student> students, MyPredicate myPredicate) {
List<Student> list = new ArrayList<>();
for (Student student : students) {
if (myPredicate.test(student)) {
list.add(student);
}
}
return list;
}
// 分?jǐn)?shù)大于95
@Test
public void test04() {
List<Student> students = filterStu(this.students, new ScopePredicate());
for (Student student : students) {
System.out.println(student);
}
}
此時(shí)周蹭,根據(jù)需求的不同趋艘,我們只需要定義MyPredicate
的實(shí)現(xiàn)類來編寫過濾條件即可,不要修改原有的邏輯代碼
優(yōu)化方式二:匿名內(nèi)部類
需求:年齡大于16
// 年齡大于16
@Test
public void test05() {
List<Student> students = filterStu(this.students, new MyPredicate<Student>() {
@Override
public boolean test(Student stu) {
return stu.getAge() > 16;
}
});
for (Student student : students) {
System.out.println(student);
}
}
需求變更:分?jǐn)?shù)大于95
// 分?jǐn)?shù)大于95
@Test
public void test06() {
List<Student> students = filterStu(this.students, new MyPredicate<Student>() {
@Override
public boolean test(Student stu) {
return stu.getScope() > 95;
}
});
for (Student student : students) {
System.out.println(student);
}
}
通過匿名內(nèi)部類的方式實(shí)現(xiàn)過濾代碼凶朗,可以避免為每一個(gè)需求定一個(gè)實(shí)現(xiàn)類來編寫過濾代碼瓷胧;但是此時(shí)的代碼還是略顯臃腫
優(yōu)化方式三:lambda表達(dá)式
需求:年齡大于16
@Test
public void test07() {
List<Student> students = filterStu(this.students, x -> x.getAge() > 16);
students.forEach(System.out::println);
}
需求變更:分?jǐn)?shù)大于95
@Test
public void test08() {
List<Student> students = filterStu(this.students, x -> x.getScope() > 95);
students.forEach(System.out::println);
}
此時(shí),我們可以看出棚愤,有效代碼只有x -> x.getAge() > 16
,x -> x.getScope() > 95
,極大提高代碼的簡(jiǎn)潔性和可讀性
語(yǔ)法
Java8中引入了一個(gè)新的操作符->
搓萧,該操作符稱為箭頭操作符或Lambda操作符,->
將Lambda表達(dá)式拆分成左右兩部分
左側(cè):Lambda表達(dá)式的參數(shù)列表(對(duì)應(yīng)匿名函數(shù)宛畦,重寫方法的參數(shù)列表)
右側(cè):Lambda表達(dá)式中所執(zhí)行的功能瘸洛,即Lambda體(對(duì)應(yīng)匿名函數(shù),重寫方法的方法體)
語(yǔ)法格式一:無參數(shù)次和,無返回值
() -> System.out.println("Hello lambda");
@Test
public void test01() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
};
r.run();
System.out.println("---------------------");
Runnable r1 = () -> System.out.println("Hello lambda");
r1.run();
}
語(yǔ)法格式二:有一個(gè)參數(shù)反肋,無返回值
(x) -> System.out.println(x);
// 若只有一個(gè)參數(shù),小括號(hào)可以不寫
x -> System.out.println(x);
語(yǔ)法格式三:多個(gè)參數(shù)踏施,有返回值石蔗,多條執(zhí)行語(yǔ)句
@Test
public void test02() {
// 匿名函數(shù)方式
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println("-------------------------------");
// 多條執(zhí)行語(yǔ)句
Comparator<Integer> comparator1 = (o1, o2) -> {
System.out.println(o1 + "--------" + o2);
return Integer.compare(o1, o2);
};
System.out.println("-------------------------------");
// 若只有一條執(zhí)行語(yǔ)句,return和大括號(hào)可以省略不寫
Comparator<Integer> comparator2 = (o1, o2) -> Integer.compare(o1, o2);
}
函數(shù)式接口
接口中只有一個(gè)抽象方法的接口畅形,稱為函數(shù)式接口养距,可以使用注解@FunctionalInterface
修飾;Lambda表達(dá)式需要“函數(shù)式接口”的支持
內(nèi)置四大核心函數(shù)式接口
接口 | 接口說明 | 參數(shù)類型 | 返回類型 | 用途 |
---|---|---|---|---|
Consumer<T> | 消費(fèi)型接口 | T | void | 對(duì)類型為T的對(duì)象操作日熬,方法:void accept(T t); |
Supplier<T> | 供給型接口 | 無 | T | 返回T類型對(duì)象棍厌,方法:T get(); |
Function<T,R> | 函數(shù)型接口 | T | R | 對(duì)T類型對(duì)象操作,并返回R類型結(jié)果碍遍;方法:R apply(T t); |
Predicate<T> | 斷言型接口 | T | boolean | 確定T類型對(duì)象是否滿足某中約束,并返回boolean值阳液;方法:test(T t); |
擴(kuò)展接口
接口 | 參數(shù)類型 | 返回類型 | 用途 |
---|---|---|---|
BiFunction<T, U, R> | T, U | R | 對(duì)類型為T, U參數(shù)應(yīng)用操作怕敬,返回R類型的結(jié)果;方法 R apply(T t, U u); |
UnaryOpetator<T> (Function子接口) | T | T | 對(duì)類型為T的對(duì)象進(jìn)行一元運(yùn)算帘皿,并返回T類型的結(jié)果东跪;方法:T apply(T t); |
BinaryOperator<T> (BiFunction子接口) | T, T | T | 對(duì)類型為T的對(duì)象進(jìn)行二元運(yùn)算,并返回T類型的結(jié)果;方法:T apply(T t1, T t2); |
BiConsumer(T, U) | T, U | void | 對(duì)類型為T, U參數(shù)應(yīng)用操作虽填;方法:void accept(T t, U u) |
ToIntFunction<T><br />ToLongFunction<T><br />ToDoubleFunction<T> | T | int<br />long<br />double | 分別計(jì)算int, long, double值的函數(shù) |
IntFunction<R><br />LongFunction<R><br />DoubleFunction<R> | int<br />long<br />double | R | 參數(shù)分別為int, long, double 類型的函數(shù) |
方法引用
若Lambda體中的內(nèi)容有方法已經(jīng)實(shí)現(xiàn)丁恭,我們可以使用方法引用
;方法引用
可以理解為是Lambda表達(dá)式的另外一種表現(xiàn)形式
對(duì)象::實(shí)例方法名
注意:Lambda 體中調(diào)用方法的參數(shù)列表和返回值類型斋日,要與函數(shù)式接口中抽象方法的參數(shù)列表和返回值類型保持一致
public void test01() {
// 方式一
Consumer<String> c1 = (x) -> System.out.println(x);
// 方式二
PrintStream out1 = System.out;
Consumer<String> c2 = (x) -> out1.println(x);
// 方式三
PrintStream out2 = System.out;
Consumer<String> c3 = out2::println;
// 方式四
Consumer<String> c4 = System.out::println;
}
類名::靜態(tài)方法名
注意:Lambda 體中調(diào)用方法的參數(shù)列表和返回值類型牲览,要與函數(shù)式接口中抽象方法的參數(shù)列表和返回值類型保持一致
public void test02() {
// 方式一
Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);
// 方式二
Comparator<Integer> comparator2 = Integer::compare;
}
類名::實(shí)例方法名
注意:Lambda表達(dá)式參數(shù)列表中第一參數(shù)是實(shí)例方法的調(diào)用者,第二個(gè)參數(shù)是實(shí)例方法的參數(shù)時(shí)恶守,可以使用ClassName::Method
@Test
public void test03() {
// 方式一
BiPredicate<String, String> predicate01 = (x, y) -> x.equals(y);
// 方式二
BiPredicate<String,String> predicate02 = String::equals;
}
構(gòu)造器引用
格式: ClassName::new
注意:Lambda體中需要調(diào)用構(gòu)造器的參數(shù)列表要與函數(shù)式接口中抽象方法的參數(shù)列表保持一致
@Test
public void test04() {
// 無參
Supplier<Date> s1 = () -> new Date();
Supplier<Date> s2 = Date::new;
// 一個(gè)參數(shù)
Function<Long, Date> s3 = (x) -> new Date(x);
Function<Long, Date> s4 = Date::new;
}
數(shù)組引用
格式:Type[] ::new
@Test
public void test05() {
// 方式一
Function<Integer, String[]> f1 = (x) -> new String[x];
// 方式二
Function<Integer, String[]> f2 = String[]::new;
}