引言:
在面向过程程序语言中,参数的传递是基本类型的变量;
在面向对象语言中,参数的传递可以是基本类型的变量或者对象变量;
在java8中又新增了传递类型,传递参数可以是方法或者代码块,即以匿名函数的形式传递。
使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口的临时实现。
首先我们要先了解什么是函数式接口:
①是一个接口,复合Java接口的定义
②只包含一个抽象方法的接口
③有且仅有一个待实现的方法,所以lambda可以自动填充上这个尚未实现的方法
④可以包括其他的default方法、static方法、private方法
⑤使用@FunctionalInterface注解可以检测是否符合函数式接口定义
一、自定义Lambda表达式:
形式一:(参数)+箭头+一个表达式
原本需要单独定义的一个排序类,现在可以作为一个匿名函数直接传入到Arrays.sort([ ],comparator)中
import java.util.Comparator;
public class MyComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
int len1=(o1==null?0:o1.length());
int len2=(o2==null?0:o2.length());
return len1-len2;
}
}
public static void main(String[] args) {
String[] planets=new String[]{"mercury","tom","mars","plane","xjy","zq"};
// Arrays.sort(planets,new MyComparator());
Arrays.sort(planets,(first,second)->(-1)*(first.length()-second.length()));//改写为lambda表达式
System.out.println(Arrays.toString(planets));
}
形式二:(参数)+箭头+{多条语句}
形式一箭头后的表达式也可以拆分成两句,但必须要加一对{ }
Arrays.sort(planets,(first,second)->{int result=(-1)*(first.length()-second.length());return result;});
形式三(参数列表无参数):( )+箭头+{表达式}
就拿最经典的创建一个线程来说,run()中无参数,用一对()表示
public class ThreadDemo {
public static void main(String[] args) {
// 普通写法
// new Thread(new Runnable() {
// @Override
// public void run() {
// int sum=0;
// for (int i=1;i<=100;i++){
// sum=sum + i;
// }
// System.out.println("总和"+sum);
// }
// }).start();
// lambda表达式写法
new Thread(()->{
int sum=0;
for (int i=1;i<=100;i++){
sum=sum + i;
}
System.out.println("总和"+sum);}).start();
}
}
形式四(一个参数,可省略括号):参数+箭头+{表达式}
这里我们定义一个接口
public interface Adder {
int selfAdd(int x);
}
接口实现
public class AdderImpl {
//lambda表达式写法
Adder c2=x->{
if (x>0)
return x+1;
return 0;
};
}
class AdderImpl2 implements Adder{
//普通写法
@Override
public int selfAdd(int x) {
if (x>0)
return x+1;
return 0;
}
}
自定义Lambda表达式总结:
1、类似于匿名方法,一个没有名字的方法
2、可以忽略参数类型(我写的所有形式都忽略了参数类型)
3、不声明返回值类型
4、没有public/protected/private/static/final等修饰符
5、单句表达式,将直接返回值,不用写大括号
6、带return语句,算多句表达式,必须用大括号{ }
二、Lambda表达式的方法引用:
如果采用大量重复性的函数式接口,会使源码过于膨胀,维护效率过低,并且自定义的lambda表达式也不便于理解,
这个时候我们就需要一种简介明了的形式来表示lambda表达式,这就是方法引用,它允许lambda表达式传递现有的类库函数。
先介绍几种常用的Java自带的函数式接口:
接口 | 参数 | 返回值 |
---|---|---|
Predicate<T> | T | Boolean |
Consumer<T> | T | void |
Function<T,R> | T | R |
Supplier<T> | None | T |
使用案例:
import org.junit.Test;
import java.util.*;
import static java.lang.Math.floor;
import static java.lang.Math.random;
public class FunctionalInterfaceTest {
private String[] planets =new String[]{"xyy","xx","yxyx","x"};
@Test
public void consumerInterface(){
Consumer<String> printer = s -> System.out.println("name :"+s);
for (String p:planets){
printer.accept(p);
}
}
@Test
public void predicateInterface() {
Predicate<String> evenLength = s -> {
return s.length()%2==0?true:false;
};
for (String p:planets){
if (evenLength.test(p)){
System.out.println(p);
}
}
}
@Test
public void SupplierInterface() {
Supplier<String> planetFactory = ()->planets[(int) floor(random()*4)];
for (int i=0;i<4;i++){
System.out.println(planetFactory.get());
}
}
@Test
public void FunctionInterface() {
Function<String,String> upper =s -> {
return s.toUpperCase();
};
for (String p:planets){
System.out.println(upper.apply(p));
}
}
}
为什么要介绍自带得函数式接口呢,就是为了避免大量的自定义函数式接口让代码显得冗余。
方法引用的几种形式:
形式一:class::staticMethod 例:Math::abs
public interface NumFunction {
double calculate(double num);
}
public class ClassStaticMethod {
public static double worker(NumFunction nf,double num){
return nf.calculate(num);
}
public static void main(String[] args) {
double a=-5.3;
double b=worker(Math::abs,a); //Math中的abs方法会自动变成NumFunction nf接口的实现,此时calculate(num)方法变成Math.abs(num)
System.out.println(b);
double c=worker(Math::floor,a);
System.out.println(c);
}
}
形式二:class::instanceMethod 例:String::compareToIgnoreCase
import java.util.Arrays;
public class ClassInstanceMethod {
public static void main(String[] args) {
String[] plannets= new String[]{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
Arrays.sort(plannets,String::compareToIgnoreCase); //这里String::compareToIgnoreCase等价与(x,y)->x.compareToIgnoreCase(y)
System.out.println(Arrays.toString(plannets));
}
}
形式三:object::instanceMethod 例:System.out::println
public interface PrintFunction {
void exec(String s);
}
public class ObjectInstanceMethod {
public static void worker(PrintFunction pf,String a){
pf.exec(a);
}
public static void main(String[] args) {
String a="xjy";
// worker(x->System.out.println(x),a);
worker(System.out::println,a);
}
}
此外,还支持this::instanceMethod调用或者super::instanceMethod调用
public class ThisInstanceMethod extends Father{
public static void main(String[] args) {
new ThisInstanceMethod().test();
}
public void test(){
String[] plannets= new String[]{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
// Arrays.sort(plannets,this::lengthCompare);
Arrays.sort(plannets,super::lengthCompare);
System.out.println(Arrays.toString(plannets));
}
public int lengthCompare(String first,String second){
return first.length()-second.length() ;
}
}
class Father{
public int lengthCompare(String first,String second){
return first.length()-second.length();
}
}
形式四:class::new / class[ ]::new 调用某类的构造函数,创建某类的单个对象或对象数组。
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
public class Person {
private String name;
private int age;
public Person() {
this.name="tom";
this.age=18;
}
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 static void main(String[] args) {
// Supplier<Person> s=()->new Person();
Supplier<Person> s=Person::new;
Person person = s.get();
System.out.println(person.getName());
IntFunction<int[]> intArray=int[]::new;
int[] nums=intArray.apply(10);
Function<Integer,Person[]> personArray=Person[]::new;
Person[] persons= personArray.apply(5);
}
}
使用lambda表达式的注意点与建议:
①再重载调用时,系统会自动依据重载的规则和参数类型推理调用合适的接口。
②lambda表达式和匿名内部类/局部内部类一样,可以共享外部一层嵌套块的变量,但是变量必须是final或者不会被改变的非final对象。
public class LambdaScopeTest {
int x=0;
class FirstLevel{
int x=1;
void methodInFirstLevel(int x){
Consumer<Integer> myConsumer=(y)->{
System.out.println("x="+x);
System.out.println("y="+y);
System.out.println("this.x="+this.x);
System.out.println("lambdaScopeTest.this.x="+LambdaScopeTest.this.x);
};
myConsumer.accept(x);
}
}
public static void main(String[] args) {
LambdaScopeTest lst=new LambdaScopeTest();
FirstLevel fl=lst.new FirstLevel();
fl.methodInFirstLevel(22);
// List<String> list=new ArrayList<>();
// for (int i=0;i<10;i++){
// list.add("xjy"+i);
// }
// list.forEach(System.out::println);
}
}
③lambda表达式不可以声明与外部嵌套块同名的参数或局部变量。
④lambda比嵌套类优先级高
⑤方法引用比自定义lambda表达式的优先级高
⑥应坚持使用标准的函数式接口