lambda表达式
1.简介
介绍: Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达 式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看 起来更简洁易懂。Lambda表达式同时还提升了对集合、框架的迭代、 遍历、过滤数据的操作。
特点:1:函数式编程 2:参数类型自动推断 3:代码量少,简洁
场景: 只有一个抽象方法(Object类中的方法除外)的接口是函数式接口,Supplier 代表一个输出,Consumer 代表一个输入,BiConsumer 代表两个输入,Function 代表一个输入,一个输出(一般输入和输出是不同类型的),UnaryOperator 代表一个输入,一个输出(输入和输出是相同类型的),BiFunction 代表两个输入,一个输出(一般输入和输出是不同类型的),BinaryOperator 代表两个输入,一个输出(输入和输出是相同类型的)
方法的引用:方法引用是用来直接访问类或者实例的已经存在的方法或者构造 方法,方法引用提供了一种引用而不执行方法的方式,如果抽象 方法的实现恰好可以使用调用另外一个方法来实现,就有可能可 以使用方法引用
静态方法引用:如果函数式接口的实现恰好可以通过调用一个静 态方法来实现,那么就可以使用静态方法引用 ▪ 实例方法引用:如果函数式接口的实现恰好可以通过调用一个实 例的实例方法来实现,那么就可以使用实例方法引用 ▪ 对象方法引用:抽象方法的第一个参数类型刚好是实例方法的类 型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函 数式接口的实现能由上面说的实例方法调用来实现的话,那么就 可以使用对象方法引用 ▪ 构造方法引用:如果函数式接口的实现恰好可以通过调用一个类 的构造方法来实现,那么就可以使用构造方法引
2. 具体代码实现
定义一个接口,该接口只有一个静态方法(除了object方法和default方法),除此之外没有其他的任何方法,定义一个类实现该方法
/**定义一个接口*/ public interface Compare { List<String> compare(List<String> list); } /**实现类,挑选出list中长度>3的字符串*/ class Compare1 implements Compare{ @Override public List<String> compare(List<String> list) { List<String>sortList=new ArrayList<>(); for(int i=0;i<list.size();i++){ if(list.get(i).length()>3){ sortList.add(list.get(i)); } } return sortList; } }
对接口的方法实现由如下几种方式:
@Test public void Test(){ List<String>str= Arrays.asList("asaddasd","asd","asdas","asdasd","s","s"); /**调用方法一*/ Compare compare=new Compare() { @Override public List<String> compare(List<String> list) { return list; } }; System.out.println("compare = " + compare.compare(str)); /**调用方法二*/ Compare compare1=new Compare1(); System.out.println("compare = " + compare1.compare(str)); /**调用方法三*/ Compare compare2=(list) -> { List<String>kis=Arrays.asList("小白"); return kis;}; System.out.println("compare = " + compare2.compare(str)); }
其中:调用方法二:通常的调用方式
调用方法一:直接在new 接口类的同时实现该方法,和方法二相比,不需要每次创建一个接口的子类
调用方法三:使用Lambda表达式,实现对数据的处理,和前面两种相比,编写代码比较简单,同样都是对接口的唯一方法的实现而已,通俗的来看,就是将接口的某个方法用Lambda的方式实现,不需要再去创建子类实现或者new Interface直接方法中处理
使用Lambad表达式需要注意的点:
1.():其中的参数的类型可以写,也可以不写,比如(Object o)-> o 或者 (o)->o 都表示输入一个参数,将该参数不做处理返回;
2. {} :当接口的方法类型为void或者只有一条语句的话,可以不适用{},比如 (o)->System.out.println(o) 或者 ()-> "有返回值,且为字符串返回值,表示接口的返回值也为String类型"
3. return :接口的方法有返回值时,如果是单条语句,直接返回 比如:()-> "baier" 当是一个语句块时,需要用{},并且{}中的rerurn之后的‘;’注意不要丢,比如:()->{ return "sdfsdf";};
3.使用方便之处
我们可以自定义一个函数式接口,然后在各个代码中使用该接口
注意:函数式接口我们可以使用@FunctionalInterface 注解来标识,比如,如果你创建了一个函数式接口,你可以在接口上方加该注解,目的是可以提示看你代码的人,还有就是检查你写的函数式接口是否正确
除了default方法之外,只有一个抽象方法,当增加一个抽象方法时,@FunctionalInterface注解会爆异常
package com.bjmashibing.system.baierhu; @FunctionalInterface public interface MyInterface { void add(); default String getName(){ return ""; } }
我们可以在其地方使用该接口:由于我们的接口是无返回值,无参数的,所以可以使用如下方式调用
@Test public void Test1(){ MyInterface myInterface=()->{List<String>list=Arrays.asList("bai","er","huuu"); System.out.println("list = " + list);}; myInterface.add(); }
可以看出,只要满足参数和返回值条件,方法具体实现可以随意编写,就好像我有返回值的某些方法块,一般我们需要些方法,这里我们可以不再写方法,直接用Lambad表达式完成方法的功能,唯一的就是需要提前写好函数式接口
不过,在jdk中已经提供了一些编写好的函数式接口,比如:Supplier 代表一个输出,Consumer 代表一个输入,BiConsumer 代表两个输入,Function 代表一个输入,一个输出(一般输入和输出是不同类型的),UnaryOperator 代表一个输入,一个输出(输入和输出是相同类型的),BiFunction 代表两个输入,一个输出(一般输入和输出是不同类型的),BinaryOperator 代表两个输入,一个输出(输入和输出是相同类型的),我们可以直接使用这些函数式接口实现我们的某些代码
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
这个是Supplier的源代码,一个输出指的是有一个输出结果,输出类型为泛型<T>,具体使用如下:
@Test public void getSupplier(){ Supplier<String> supplier=()->"baier"; System.out.println("supplier = " + supplier.get()); }
返回值为String类型,无参数,只要保证这两点,就可以使用Supplier接口的get方法返回Lambda表达式的返回值。
再比如BinaryOperator 接口,源代码如下:
@FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T>
继承了BiFunction,BiFunction源代码如下:
@FunctionalInterface public interface BiFunction<T, U, R> { R apply(T t, U u); default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t, U u) -> after.apply(apply(t, u)); } }
由此可以看出,BinaryOperator是在BiFunction的基础上将两个输入类型和一个输出类型的类型定为了同一个,上述代码中的标红部分
假设我们要处理参数是两个,返回值是两个,但是参数和返回值的类型相同的这种需求,不需要再写其他方法,直接使用lambad即可,比如两个数值的相加
@Test public void testBinaryOperator(){ BinaryOperator<Integer> bi=(num1,num2)->num1+num2; int i=bi.apply(12,34); System.out.println(i); }
总体而言:使用jdk中已经编写好的函数式接口和lambad表达式结合,可以减少代码的开发量
4.方法的引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用,
静态方法引用:如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用
实例方法引用:如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用
对象方法引用:抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用
构造方法引用:如果函数式接口的实现恰好可以通过调用一个类 的构造方法来实现,那么就可以使用构造方法引
对象方法的使用使得方法的调用编写在某直程度上变的简单:
1.静态方法的引用:
/**定义静态方法*/
public static Integer getAge(String name){ return name.length(); } public static Integer getAge(){ return 0; }
/**调用静态方法*/
@Test public void testStatic(){ /**1*/ Function<String,Integer>fun=(str)->getAge(str); /**2*/ Function<String,Integer>fun1=(str)->MainTesrt.getAge("baierhu"); /**3*/ Function<String,Integer>fun2=MainTesrt::getAge; /**4*/ Function<String,Integer>fun3=MainTesrt::getAge; System.out.println("fun = " + fun.apply("baierhu")); System.out.println("fun1 = " + fun1.apply("baierhu")); System.out.println("fun2 = " + fun2.apply("baierhu")); System.out.println("fun3 = " + fun3.apply("baierhu")); }
输出结果相同,也就是说,通常的静态方法调用由普通的类名.方法名可以变成现在上述的写法,尤其是3,4中的使用,利用函数式接口和静态方法的引用相互结合的方式
2 .实例方法引用
/**方法*/
public void add(String arr){ System.out.println("arr.length() = " + arr.length()); }
/**方法的调用*/
@Test public void testConsumer(){ /**3*/ Consumer<String>consumer3=new MainTesrt()::add; consumer3.accept("consumer"); }
3.对象方法引用
/**定义一个类,类中定义两个通常的方法*/ class Student{ public int getAge(String age){ System.out.println("age = " + this); return age.length(); } public int getAge(Integer age){ System.out.println("age = " + this); return age; } }
/**方法的调用方式:*/
@Test public void testObject(){ BiFunction<Student,String,Integer> function=(stu,str)->stu.getAge(str); /**函数式接口的类型为实例方法的类型时,对应输入输出就可以使用如下的方法编写程序*/ BiFunction<Student,Integer,Integer> function1=Student::getAge; BiFunction<Student,String,Integer> function2=Student::getAge; Student student=new Student(); System.out.println("function = " + function.apply(student,"baierhu")); System.out.println("function = " + function1.apply(student,232)); System.out.println("function = " + function2.apply(student,"baierhu")); }
运行结果表示:方法的引用只是创建一个引用方式,真正执行时重要的还是传入的参数,比如当变量student传入同一个时表示传入同一个对象
4.构造方法引用
class Student{ private String age; public Student(String age){ this.age=age; } public Student(){} public int getAge(String age){ System.out.println("age = " + this); return age.length(); } public int getAge(Integer age){ System.out.println("age = " + this); return age; } public String getAge(){ return age; } }
/***构造方法的引用*/
@Test public void construcateTest(){ Supplier<Student> supplier=()->new Student(); /**0*/ Supplier<Student> supplier1=Student::new; /**1*/ Function<String,Student>studentStudentBiFunctio=Student::new; /**2*/ Function<String,Student>studentStudentBiFunction=(stu)->new Student(stu); System.out.println("supplier1 = " + studentStudentBiFunction.apply("sdfsdf").getAge()); System.out.println("supplier1 = " + studentStudentBiFunctio.apply("dsfsd").getAge()); supplier1.get(); }
如上代码,当有无参数的构造方法时,我们可以使用使用0中的代码代替new对象的方式编写,有意思的是,0和1相比较,1中可以传递参数,在使用接口式函数的方法时,传入参数,参数会传入到构造方法中
截止目前为止:个人感觉使用起来lambad表达式,在很多情况下其实并不是很方便,它在功能上并没有增强什么,只是在编写程序方面提供了些许便利,但大多数情况下好像并不是很理想!