什么是Lambda?
Lambda是函数编程,所以Lambda传递的是“函数”,函数是Java的新类型吗?不是,Java里面函数不妨叫做函数接口(functional interface)。他满足这样的条件:首先他的类型是interface,而且有且仅有一个抽象方法,然后有N(N>=0)个default方法和static方法。
public interface InterfaceTest {
void test(); //这个接口里只有一个方法
}
public static void main(String[] args) {
InterfaceTest l1 =()->{System.out.println("1");};
}
这里可以看到Lamda表达式的作用就是可以对接口方法进行非常简洁的实现,且不需要实现类。
Lambda对接口的要求
虽然可以使用Lambda表达式对某些接口进行简单的实现,但是并不是所有的接口都可以用Lambda表达式来实现,要求接口中定义的必须要实现的抽象方法只能是一个。
在java8中接口还添加了一个新关键字:default , 使用这个修饰符可以为接口添加默认实现。
public interface InterfaceTest {
default void test2(){
System.out.println(2);
};
}
public static void main(String[] args) {
InterfaceTest l1 =null;
l1.test2(); //正常输出2,并不会报空指针异常哦
}
Lambda的使用
先上一段测试用的接口,每个接口都只有一个方法
/**
* 无参数无返回值接口
*/
public interface LambdaNoneReturnNoneParmeter {
void test();
}
/**
* 无返回值有单个参数
*/
@FunctionalInterface //修饰函数式接口
public interface LambdaNoneReturnSingleParmeter {
void test(int n);
}
/**
* 无返回值 多个参数的接口
*/
@FunctionalInterface
public interface LambdaNoneReturnMutipleParmeter {
void test(int a,int b);
}
/**
* 有返回值 无参数接口
*/
@FunctionalInterface
public interface LambdaSingleReturnNoneParmeter {
int test();
}
/**
* 有返回值 有单个参数的接口
*/
@FunctionalInterface
public interface LambdaSingleReturnSingleParmeter {
int test(int n);
}
/**
* 有返回值 有多个参数的接口
*/
@FunctionalInterface
public interface LambdaSingleReturnMutipleParmeter {
int test(int a,int b);
}
然后就是挨个实现
public static void main(String[] args) {
LambdaNoneReturnNoneParmeter lam_1 = ()->{
System.out.println("无参数无返回值接口");
};
LambdaNoneReturnSingleParmeter lam_2 = (int a)->{
System.out.println("无返回值有单个参数接口");
};
LambdaNoneReturnMutipleParmeter lam_3 = (int a,int b)->{
System.out.println("无返回值 多个参数的接口");
};
LambdaSingleReturnNoneParmeter lam_4 = ()->{
System.out.println("有返回值 无参数接口");
return 0;
};
LambdaSingleReturnSingleParmeter lam_5 = (int q)->{
System.out.println("有返回值 有单个参数的接口");
return 0;
};
LambdaSingleReturnMutipleParmeter lam_6 = (int ia,int ip)->{
System.out.println("有返回值 有多个参数的接口");
return ia+ip;
};
int num1 = lam_6.test(13, 21);
System.out.println(num1);
}
可以看到,这里的Lamda表达式虽然可以实现接口方法,但总体来说还是不够整洁,接下来我们看下如何更加简洁的实现
Lambda的使用(简洁版)
1.参数类型精简 , 因为在接口中已经定义过了方法的参数,所以不需要再写参数类型
LambdaSingleReturnMutipleParmeter lam_1 = (int a,int b)->{return a+b;};
可以这样写---->
LambdaSingleReturnMutipleParmeter lam_1 = (a,b)->{return a+b;};
2.参数小括号精简 ,如果参数列表中,参数的数量只有一个,此时小括号可以省略,注意,如果有多个参数则不可以省略
LambdaSingleReturnSingleParmeter lam_2 = (a)->{return a;};
可以这样写---->
LambdaSingleReturnSingleParmeter lam_2 = a->{return a;};
3.方法大括号精简,如果方法体中只有一条语句,就可以省略大括号。
LambdaNoneReturnSingleParmeter lam_2 = a ->{System.out.println(1);};
可以这样写---->
LambdaNoneReturnSingleParmeter lam_2 = a ->System.out.println(1);
4.大括号精简补充,如果只有一条语句,且是返回值的话,想要省略大括号就必须省略return。
LambdaSingleReturnSingleParmeter lam_3 = a->{return 10;};
可以这样写---->
LambdaSingleReturnSingleParmeter lam_3 = a->10;
Lambda语法进阶
可以看到,使用Lamda表达式实现方法后,赋值给了一个对象。这也意味着我们可以通过这种方法将一个实现过的方法赋值给这些对象。
1.方法引用(普通方法与静态方法)
语法:方法的隶属者::方法名
- 注意:
- 1.引用的方法中,参数数量和类型一定要和接口中定义的方法一致
- 2.返回值的类型也一定要和接口中的方法一致
- 3.这个方法的返回值不一定是隶属类的类型
然后调用时,方法名还是原先接口定义的方法名,而不是赋值的那个静态方法名
public class Lamda1 {
public static void main(String[] args) {
//普通方法引用
LambdaSingleReturnSingleParmeter lam_4 = a->change(a);
//静态方法引用
LambdaSingleReturnSingleParmeter lam_5 = Lamda1::change;
//接口的方法名
System.out.println(lam_5.test(10));
}
private static int change(int a){
return a*2;
}
}
2.方法引用(构造方法)
先创建一个实体类
public class Person {
public String name;
public int age;
public Person() {
System.out.println("Person的无参构造方法执行");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person的有参构造方法执行");
}
}
再创建两个接口
interface PersonCreater{
//通过Person的无参构造实现
Person getPerson();
}
interface PersonCreater2{
//通过Person的有参构造实现
Person getPerson(String name,int age);
}
public static void main(String[] args) {
//引用的是Person的无参构造
//PersonCreater接口的方法指向的是Person的方法
PersonCreater creater=()->new Person();
//等价于上面的()->new Person()
PersonCreater creater1=Person::new;
//实际调用的是Person的无参构造 相当于把接口里的getPerson()重写成new Person()。
Person a=creater1.getPerson();
//引用的是Person的有参构造
PersonCreater2 creater2=Person::new;
Person b=creater2.getPerson("张三",18);
}
简单概括下就是这两种方法可以引用构造方法
PersonCreater creater=()->new Person();
PersonCreater creater1=Person::new;
注意:是引用无参构造还是引用有参构造 在于接口定义的方法参数
基本函式数接口
基本的函数式接口主要有四个:
① Supplier 生产者:无输入,生产一个T类型的值;
② Consumer 消费者:输入一个T类型的值,无输出;
③ Function<T,R> 函数:输入一个T类型的值,返回一个R类型的值;
④ Predicate 断言:输入一个T类型的值,返回true/false
————————————————
转自https://blog.csdn.net/Alexshi5/article/details/95752331
public void example2(){
//accept 接收参数并调用接口的方法
Consumer<Integer> consumer = x -> System.out.println(x);
consumer.accept(15); //打印:15
//andThen 先调用原Consumer,再调用andThen方法中指定的Consumer
Consumer<Integer> secondConsumer = y -> System.out.println(y + y);
consumer.andThen(secondConsumer).accept(10); //打印: 10 20
}
这里我们可以看到第四行代码打印结果是10和20,可明明是先执行andThan()方法再执行accept(),根据前面写的Lamda表达式,不应该先输出10吗?这里我们需要看看andThen方法的源码
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
可以看到@FunctionalInterface我们就知道,这是个函数接口,所以只能有一个抽象方法accept(T t)。至于第二个方法它已经给出了默认实现,Objects.requireNonNull(after);的作用是判空,返回的结果是一个Lamda表示式,可是奇怪的是这个方法的返回值不是Consumer吗,怎么就变成了函数呢?这也就说明了这个Lamda表达式是个Consumer类型的函数变量,既然是Consumer变量自然就可以调用accept方法了。说到这我们再来看看这个Lamda表达式的作用,它连续调用了两次accept方法,但是是不同对象调用的。第一次是this.accept() , 第二次是after.accept(t) 也就是我们传进来的参数调用的。
编译器先去检查这个方法的返回值,返回值是Consumer接口,它有一个抽象方法,可以存放Lamda函数,所以返回的时候这个Lamda表示式默认存放到了一个Consumer里,然后再返回该Consumer。
简单来说就是遇见返回值是Lamda表达式时,先去检查该方法返回值,这个返回值类型一定是函数接口
也就是说这个方法的执行顺序是andThen先返回了一个会执行自身的accep方法,再执行传进来的那个参数的accept方法