Lambda表达式介绍
Lambda表达式是Java8中添加的新功能。使用Lambda表达式可以替代只有一个抽象函数接口实现,告别匿名内部类,代码看起来更简洁。Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作。
函数式接口
只有一个抽象方法的接口称为函数式接口,在使用该类接口时可以用lambda表达式对该类接口的实现进行简化
案例
//注解,可自定义
@FunctionalInterface
public interface InterfaceTest{
public void add();
}
Runnable接口也是函数式接口之一
Lambda表达式的使用
Lambda表达式也称为匿名函数,下面为最基本的使用方式
Lambda表达式的基本格式
x,y表示参数,可以有,也可以没有,参数类型可以由JVM自动推断,也可以显示写出,多个参数用逗号分隔, ->操作符后面的是执行的逻辑代码,一行以内可以不加花括号,多行需要加上花括号,只有一个参数时可以不加小括号,多于一个或0个参数都必须加小括号
(x,y)->{System.out.println(x+y)};
()->{};
()->{System.out.println(1);};
()->System.out.println(1);
()->{return 100;};//有return关键字必须加花括号
()->100;//无return关键字,但直接返回100
()->null;//无return关键字,直接返回null
(int x)->{return x+1;};
(int x)->x+1;//显示声明类型返回x+1后的结果
(x)->x+1;//JVM自动推导类型返回x+1后的结果
x->x+1;//只有一个参数可以不加小括号,非规范写法
lambda表达式最直观的简化
lambda对函数式接口进行简化时的返回值,需与lambda所简化实现的函数式接口下的的抽象函数一致!!!
简化方式如下
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;//jdk自带的函数式接口
import java.util.function.Consumer;//jdk自带的函数式接口
import java.util.function.Function;//jdk自带的函数式接口
import java.util.function.Supplier;//jdk自带的函数式接口
public class LambdaTest {
public static void main(String[] args) throws Exception {
Runnable runnable = new Runnable() {//匿名内部类+多态的写法
@Override
public void run() {
System.out.println("running1 .....");
}
};
runnable.run();
Runnable runnable2 = ()->{//lambda+多态 = 声明一个匿名类实现Runnable接口的run方法,并创建后赋值给runnable2
System.out.println("running2....");
};
runnable2.run();
//同上
Runnable runnable3 = ()-> System.out.println("running3....");
runnable3.run();
Callable<String> c1 = new Callable() {
@Override
public String call() throws Exception {
return "zhangsan";
}
};
System.out.println(c1.call());
//指定Callable接口需要返回一个String
Callable<String> c2 = ()->{return "lisi";};//lambda返回一个String
System.out.println(c2.call());
//同上
Callable<String> c3 = ()->"wangwu";
System.out.println(c3.call());
}
lambda表达式对匿名内部类的简化
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;//jdk自带的函数式接口
import java.util.List;
public class LambdaDemo {
public static void main(String[] args) {
//原写法
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println("running.....");
// }
// });
// thread.start();
//
//lambda表达式简化
new Thread(()->{System.out.println("running2.....");}).start();
List<String> list = Arrays.asList("java","javascript","scala","python");
//原写法
// Collections.sort(list, new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length()-o2.length();
// }
// });
// for(String str:list){
// System.out.println(str);
// }
//lambda表达式简化
Collections.sort(list,(a,b)->a.length()-b.length());
list.forEach(System.out::println);
}
}
lambda的返回值
先看下面几行lambda的实现
()->100;//无return关键字,但直接返回100
()->null;//无return关键字,直接返回null
()->get();
上面方式直接调用自定义get()方法的lambda,如果get()有返回值时,lambda有无返回值取决于它所实现的函数式接口,若lambda所实现的函数式接口的抽象方法返回值与get()的返回值不一致将报错
方法的引用
把一个方法作为参数传递,实现回调函数功能,方法的引用类似于C/C++的函数指针
如下代码,对一个list进行遍历的两种方法
List<String> list = Arrays.asList("a","b","c");
for (String s : list) {
System.out.println(s);
}
//传入printlin函数引用作为参数
list.forEach(System.out::println);//注意是::运算符,不是.运算符
同样的传入方法的引用也可以是lambda表达式所实现的方法(请记得lambda表达式也称为匿名函数),但作为方法的引用传递lambda表达式时不会立即执行,而是等待传入的方法进行调用(这里文字立即有歧义,其实如上面代码就是println等待forEach函数执行到某一处时调用println)
方法引用的分类
静态方法引用代码示例
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test1 {
static String put(){
System.out.println("put.....");
return "put";
}
public static void main(String[] args) {
// System.out.println(put());
Supplier<String> s1 = ()->Test.put();//lambda表达式实现接口
System.out.println(s1.get());
//Supplier jdk自带函数式接口,有一个get抽象函数
//作为函数引用实现Supplier的get抽象方法,调用get等同于调用put
Supplier<String> s2 = Test1::put;
System.out.println(s2.get());
//原理同上
Supplier<String> s3 = Fun::hehe;
System.out.println(s3.get());
}
}
class Fun{
public static String hehe(){
return "hehe";
}
public static String toUpperCase(String str){
return str.toUpperCase();
}
}
实例方法引用代码示例
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test2 {
public String put(){
return "put...";
}
public void getSize(int size){
System.out.println("size:"+size);
}
public static void main(String[] args) {
System.out.println(new Test2().put());
Supplier<String> s1 = ()->new Test2().put();
Supplier<String> s2 = ()->{return new Test2().put();};
Supplier<String> s3 = new Test2()::put;
System.out.println(s1.get());
System.out.println(s2.get());
System.out.println(s3.get());
//唯一的创建一个test2对象
Test2 test = new Test2();
Consumer<Integer> c1 = (size)->new Test2().getSize(size);
Consumer<Integer> c2 = new Test2()::getSize;
Consumer<Integer> c3 = test::getSize;
c1.accept(123);
c2.accept(123);
c3.accept(123);
}
}
对象方法引用代码示例
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class Test3 {
public static void main(String[] args) {
BiConsumer<Too,String> bc = (too,str)->new Too().show(str);//现在传入的参数类型为Too和String,但后面new Too()创建了一个新的临时对象调用show
BiConsumer<Too,String> bc2 = Too::show;
bc.accept(new Too(),"abc");
bc2.accept(new Too(),"def");
BiFunction<Exec,String,Integer> bf1 = (e,s)->new Exec().test(s);//new Exec()创建新对象,与前面的e没关系
BiFunction<Exec,String,Integer> bf2 = (e,s)-> e.test(s);
BiFunction<Exec,String,Integer> bf3 = Exec::test;
bf1.apply(new Exec(),"abc");
bf2.apply(new Exec(),"def");
bf3.apply(new Exec(),"ghi");
}
}
class Exec{
public int test(String name){
return 1;
}
}
class Too{
public void show(String str){
System.out.println("show ---too2"+str);
}
}
}
构造方法引用代码示例
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test4 {
public static void main(String[] args) {
Supplier<Person> s1 = ()->new Person();
Supplier<Person> s2 = Person::new;//构造方法引用
s1.get();
s2.get();//等同于调用Person();
Supplier<List> s3 = ArrayList::new;
Supplier<Set> s4 = HashSet::new;
Supplier<Thread> s5 = Thread::new;
Supplier<String> s6 = String::new;
//Supplier<Integer> s7 = Integer::new; //Integer的构造方法必须传入一个参数,所以这条语句是错的
//带参数的构造方法引用
Consumer<Integer> c1 = (age)->new Account(age);
Consumer<Integer> c2 = Account::new;
c1.accept(123);
c2.accept(456);
//另外一种方式
Function<String,Account> f1 = (str)->new Account(str);
Function<String,Account> f2 = Account::new;
f1.apply("abc");
f2.apply("def");
}
}
class Account{
public Account(){
System.out.println("调用无参构造方法");
}
public Account(int age){
System.out.println("age 参数构造" +age);
}
public Account(String str){
System.out.println("str 参数构造" +str);
}
}
class Person{
public Person(){
System.out.println("调用无参的构造方法");
}
}