一、 lambada表达式简介
我们知道对于Java变量可以赋给其一个值,而如果想将"一块代码(一个完整的方法)"赋给一个Java变量,如下所示,怎么做呢?
你可能认为就是下面的方式来实现
很显然,这个并不是一个很简洁的写法,我们采用Java8的Lambada表达式来实现,那么如何简化呢?
整个过程:去掉修饰符(public等)、去掉函数的名字(因为已经赋给变量,变量知道此方法名--往后知道抽象方法唯一,不需要
方法名了)、去掉返回值类型(编译器可以推断)、去掉参数类型(编译器可以推断参数类型),最终的结果是下面的形式:
分析:这样的最终结果就是把"一块代码赋给一个变量"。或者说是"这个被赋给一个变量的函数"就是一个Lambada表达式,由于
Lambada可以直接赋给一个"变量",我们可以把Lambada(这里表示为变量)作为参数传递给函数。
但是变量(Lambada表达式)的类型是什么呢?
说明:所有的Lambada的类型都是一个接口,而Lambada表达式本身("那段代码")就是一个接口的实现,这是理解Lambada的
一个关键所在,理解上可以这样认为:Lambada表达式就是产生一个实现接口中唯一的抽象方法的子实现类的对象,因此最终结
果:
函数式接口:接口中只有一个需要被实现的抽象函数
说明:为了避免后来的人在接口中增加新的接口函数,导致其有多个接口函数需要被实现,变成非函数式接口,引入了一个新的Annotation(注解):@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口,加上它的接口不会被编译,如果加上此标记就不能再添加其他的抽象方法,否则会报错。它有点像@Override,都是声明了一种使用意图,避免你把它用错。
总结:lambda表达式本质是匿名方法
二、 Lambda 表达式的结构
2.1 Lambada表达式的语法
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ ->”,该操作符被称为 Lambda 操作符或
箭头操作符。它将 Lambda 分为两个部分:
即:(参数列表)—>{express或statements}
左侧: 指定了 Lambda 表达式需要的方法参数列表←→右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能
2.2 使用说明
(1)一个 Lambda 表达式可以有零个或多个参数,参数的类型既可以明确声明,也可以根据上下文来推断
(2)圆括号内,方法参数列表之间用逗号相隔
(3)当只有一个参数,且其类型可推导时,圆括号()可省略
(4)Lambda 表达式的主体可包含零条或多条语句,如果 Lambda 表达式的主体只有一条语句,花括号{}可省略,如果有返回值,return也可以省略,同时body中的“;”也可以省略。匿名函数的返回类型与该主体表达式一致
(5)如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
三、简单应用
(1)对比匿名内部类做为参数传递和Lambda表达式作为参数来传递--Runnable,Callable接口(具体看例子)
匿名内部类作为参数传递和Lamada表达式作为参数传递
例1:
package org.lamada;
public class LamadaDemo0 {
public static void main(String[] args) {
//匿名内部类的形式开启一个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我爱你!");
}
}).start();
//Lambada表达式创建匿名内部类开启一个线程
new Thread(() -> System.out.println("-------------")).start();
}
}
例2
package org.lamada;
public class LamadaDemo1 {
public static void main(String[] args) {
//常见的函数式接口:Runnable、 Comparable--排序(是一个函数式接口吗?)
Comparable<Integer> comparable=new Comparable<Integer>() {
@Override
public int compareTo(Integer o) {
return 0;
}
};
//Lambada表达式的方法
Comparable<Integer> com=(a)->a;
int i = com.compareTo(3);
System.out.println(i);
}
}
例3
package org.lamada;
import java.util.Comparator;
import java.util.TreeSet;
public class LamadaDemo2 {
public static void main(String[] args) {
//TreeSet排序新高度---Comparator本身有泛型
TreeSet<Integer> tereeSet = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a + b;
}
});
//Lambada表达式的形式
new TreeSet<Integer>((a,b)->a-b);
}
}
例4 自定义一个函数式接口
package org.lamada;
@FunctionalInterface
public interface MyFunctionalInterface <T> {
void method(T s);//自定义函数式接口---注意其定义
//用一个注解 @FunctionalInterface 去检测这个接口是不是一个函数式接口
}
测试
package org.lamada;
public class LamadaDemo3 {
public static void main(String[] args) {
//首先自定义一个函数式接口:MyFunctionalInterface
new MyFunctionalInterface<String>() {
@Override
public void method(String s) {
}
}.method("你好吗?");
//Lamada表达式的用法
MyFunctionalInterface<String> lamada=(s)-> System.out.println(s);
}
}
(3)Lambda 需要函数式接口的支持,来看下我们Java1.7以前遇到的函数式接口:点击打开链接
在Java 8中有一个函数式接口的包,里面定义了大量可能用到的函数式接口(java.util.function)
(4)Java1.8中提供的一个新的一个接口函数包四大核心式接口
详见:点击打开链接,注意几个名词(消费型、供给型、函数型、断言型函数)
例5 使用function包中的函数式接口
package org.lamada;
import java.util.function.Supplier;
public class LamadaDemo4 {
public static void main(String[] args) {
new Supplier<Double>() {
@Override
public Double get() {
return 3.1;
}
};
}
}
四、方法引用
概念:方法引用其实是Lambda表达式的另一种写法,当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用。
语法:使用操作符 “ ::” 将方法名和对象或类的名字分隔开来
几种常见形式:
(1)类名::静态方法
(2)对象::实例方法
(3)类名::实例方法
注意:
* 1. Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
* 2.若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method;不管怎么说,实质还是抽象方法的实现
对比:相比而言省略参数列表,是因为二者的类型一致,主要凸显方法(重重之重!!)
应用:
package com.company;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class LambdaDemo6 {
public static void main(String[] args) {
// 方法引用 :: 是Lambda表达式的另一种简写方式
//类名::静态方法
//对象::实例方法
//类名::实例方法
Supplier<Double> supplier = new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
};
Supplier<Double> supplier2=()->Math.random();//方法引用
Supplier<Double> supplier3=Math::random;
System.out.println("-------------------------------------");
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
PrintStream out = System.out;
out.println(s);
}
};
PrintStream out = System.out;
Consumer<String> consumer2=(s)->out.println(s);
Consumer<String> consumer3=out::println;//方法引用也没有参数
consumer3.accept("bbbb");
System.out.println("------------------------------");
Comparator<String> stringComparator = new Comparator<String>(){
@Override
public int compare(String s1, String s2) {
//你对一个函数式接口中的抽象方法重写时,如果说你传的这两个参数
//一个参数作为了调用者,一个参数作为了 传入者
//那么你也可以使用方法引用来简写Lambda表达式
return s1.compareTo(s2);
}
};
Comparator<String> stringComparator2=(s1,s2)->s1.compareTo(s2);
Comparator<String> stringComparator3=String::compareTo;
System.out.println("------------------------------------------------");
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a.compareTo(b);
}
};
Comparator<Integer> comparator2=(x,y)->x.compareTo(y);
Comparator<Integer> comparator3=Integer::compareTo;
}
}
六、构造器引用----创建对象的专属
概念和应用
概念:与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口
中抽象方法的参数列表一致!
细节问题:并没有说返回值类型,因此与返回值类型没有半毛钱关系,是对象创建简化的的福音
格式:ClassName:: new
注意:需要调用的构造器方法与函数式接口中抽象方法的参数列表保持一致
应用:自定义一个标记接口MyInterface,返回值类型为自定义类MyClass
MyInterface接口
package org.lamada;
@FunctionalInterface
public interface MyInterface<T,R> {
R method(T t);//有参---可以使用Java8中已经存在的Function---具备此特性
}
MyClass类
package org.lamada;
public class MyClass {
String s=null;
public MyClass(String s) {
//有参的构造方法--无参的更简单
this.s=s;
}
}
测试类
package org.lamada;
public class LamadaDemo5 {
public static void main(String[] args) {
//(1)传统的方式
MyInterface<String,MyClass> myInterface1 = new MyInterface<String,MyClass>() {
@Override
public MyClass method(String s) {
return new MyClass(s);
}
};
//(2)Lamada方式---首先实现此方法
MyInterface<String,MyClass> myInterface2=(s)-> new MyClass(s);
//(3)构造器引用的方式---不管参数了列表了,简化方法体
MyInterface<String,MyClass> myInterface3=MyClass::new;
MyClass myclass = myInterface3.method("你好吗?");
System.out.println(myclass.s);
//不管哪种方法,调用时传实参的形式是一致的
}
}