Lambda表达式学习笔记
Lambda基础语法
Java8中引入了一个新的操作符“ -> ”,该操作符被称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分成两部分:
- 左侧:Lambda表达式的参数列表
- 右侧:Lambda表达式中所需要执行的功能,即Lambda体
Lambda表达式常见的五种语法如下:
- 语法格式一:无参数,无返回值 (如Runable接口中的run方法)
@Test
public void test01(){
Runnable run01 = new Runnable() {
@Override
public void run() {
System.out.println("通过匿名内部函数实现!");
}
};
run01.run();
Runnable run02 =() -> System.out.println("Hello Lambda!");
run02.run();
}
2. 语法格式二:有一个参数,无返回值 (如Java8中的Consumer接口)
@Test
public void test02(){
Consumer con01 = (x) -> System.out.println(x);
//若只有一个参数,小括号可以省略不写
Consumer con02 =x -> System.out.println(x);
//调用抽象方法
con01.accept("使用Lambda表达式,实现抽象方法,输出参数的值");
con02.accept("若只有一个参数,小括号可以省略不写");
}
3. 语法格式三:有两个以上参数,有返回值,并且Lambda体中有多条语句,大括号必须写
@Test
public void test03(){
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x,y);
};
int compare = com.compare(3, 6);
System.out.println(compare);//输出结果为:函数式接口 -1
}
4. 语法格式四:若Lambda体中只有一条语句,return 和 大括号 都可以省略不写
@Test
public void test04(){
Comparator<Integer> com = (x, y) -> Integer.compare(x,y);
int compare = com.compare(3, 6);
System.out.println(compare); // 输出结果为: -1
}
5. 语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”
@Test
public void test05(){
Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x,y);
int compare = com.compare(3, 6);
System.out.println(compare); // 输出结果为: -1
}
Lambad表达式需要“函数式接口“的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解@FunctionalInterface修饰,检查是否是函数式接口
注意:在jdk1.8之前,在局部内部类中,使用了同级别的局部变量,该局部变量必须使用final修饰,JDK1.8之后,该局部变量被默认使用了final修饰,仍然不允许被修改,不需要我们手动去加了
Java8 内置的四大核心函数式接口:
- 消费型接口
@FunctionalInterface
public interface Consumer<T> {
//一个参数,无返回值
void accept(T t);
}
- 供给型接口
@FunctionalInterface
public interface Supplier<T> {
//无参数,有返回值
T get();
}
- 函数性接口
@FunctionalInterface
public interface Function<T, R> {
// T代表参数,R代表返回值
R apply(T t);
}
- 断言型接口(用作一些判断操作)
@FunctionalInterface
public interface Predicate<T> {
//一个参数,返回值为boolean类型,常用来做一些判断操作
boolean test(T t);
}
下面对Java8提供四大核心函数式接口,进行简单的测试运用:
- 消费型接口测试
@Test
public void test06(){
//使用Lambda表达式,对Consumer接口中的accept()进行了具体实现
happy(10000.00,(money) -> System.out.println("今天用了10000元买了台Mac笔记本电脑"));
}
/**
* 自定义方法调用Consumer接口的抽象方法
* @param money
* @param consumer
*/
public void happy(Double money,Consumer<Double> consumer){
consumer.accept(money);
}
- 供给型接口测试
@Test
public void test07(){
//获取10个100以内的随机数
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
for(Integer num: numList){
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num,Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i=0;i<num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
- 函数型接口测试
//函数型接口:可以对参数进行一些操作,然后返回操作结果
@Test
public void test08(){
//由于Lambad体中只有一条语句,这里return 和 大括号 都省略了
String newStr = strHandler("函数型接口测试",(str) -> str.substring(3,str.length()));
System.out.println(newStr);// 输出结果为: 接口测试
}
//需求:用于处理字符串
public String strHandler(String str,Function<String,String> fun){
return fun.apply(str);
}
- 断言型接口
@Test
public void test09(){
List<String> list = Arrays.asList("hello","Lambda","easy","OK");
//获取字符串长度大于4的集合
List<String> strList = filterStr(list,(s) -> {return s.length()>4;});
System.out.println(strList); // 输出结果为:[hello, Lambda]
}
//将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list,Predicate<String> pre){
List<String> strList = new ArrayList<>();
for(String str:list){
//调用断言型接口中的方法,判断是否满足条件
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
方法引用
若Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为Lambda表达式的另一种表现形式)。
主要有三种语法格式:
- 语法格式一: 对象::实例方法名
- 语法格式二: 类名::静态方法名
- 语法格式三: 类名::实例方法名
条件:Lambda体中调用方法的参数列表和返回值类型,要与 函数式接口中抽象方法的参数列表和返回值类型保持一致
下面我们对方法引用的三种语法格式,使用代码来看看是如何使用的吧:
//对象::实例方法
@Test
public void test10(){
//使用Lambda表达式实现接口中的抽象方法
Consumer<String> con = (x) -> System.out.println(x);
con.accept("abcdef");
//使用 方法引用 的形式,实现接口中的抽象方法
//这里的 System.out 实际上是PrintStream的类型的匿名对象
//System.out::println; 也可以写成 PrintStream ps1 = System.out; ps1::println;
Consumer<String> con2 = System.out::println;
con2.accept("abcdef");
}
这里为了更好看方法引用的语法格式,我们可以创建一个Emp实体类:如下
public class Emp {
private Integer id;
private String name;
public Emp() {
}
public Emp(Integer id) {
this.id = id;
}
public Emp(String name) {
this.name = name;
}
public Emp(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接下来我们再使用Emp实体类的对象,通过 对象::实例方法 这种语法格式进行练习
@Test
public void test11(){
Emp emp = new Emp();
//使用Lambda表达式实现Supplier接口中的抽象方法
Supplier<String> sup = () -> emp.getName();
String str = sup.get();
System.out.println(str);
//使用方法引用代替Lambda表达式进行实现
//注意:这里的getName()的参数列表和返回值类型,和Supplier接口中的get()的参数列表和返回值类型是保持一致的
Supplier<String> sup2 = emp::getName;
String name = sup2.get();
System.out.println(name);
}
语法格式二:类名::静态方法名 的具体应用
@Test
public void test12(){
Comparator<Integer> com01 = (x,y) -> Integer.compare(x,y);
int compare01 = com01.compare(10, 8);
System.out.println("通过Lambda表达式的比较结果为:"+compare01);
//类名::静态方法名
Comparator<Integer> com02 = Integer::compare;
int compare02 = com02.compare(10, 8);
System.out.println("通过方法引用实现的比较结果为:"+compare02);
}
语法格式三:类名::实例方法名
使用该种语法格式时,必须要注意:若Lambda表达式参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,则可以使用ClassName::method。
@Test
public void test13(){
BiPredicate<String,String> bp01 = (x,y) -> x.equals(y);
boolean test01 = bp01.test("hello", "hello");
System.out.println("通过Lambda表达式的比较结果为:"+test01);//输出结果:通过Lambda表达式的比较结果为:true
BiPredicate<String,String> bp02 = String::equals;
boolean test02 = bp02.test("hello", "hello");
System.out.println("通过方法引用实现的比较结果为:"+test02); //输出结果:通过方法引用实现的比较结果为:true
}
构造器引用
格式:ClassName::new
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
@Test
public void test14(){
Supplier<Emp> sup = () -> new Emp();
//构造器引用方式
//调用的是无参构造:由于需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致,这里供给型结构Supplier中的get()是没有参数列表,所以调用的是无参构造
Supplier<Emp> sup02 = Emp::new;
Emp emp = sup02.get();
System.out.println(emp);
}
@Test
public void test15(){
Function<Integer,Emp> fun = (x) -> new Emp(x);
//调用的是带一个参数的构造:因为函数型接口Function中的apply()有一个参数
Function<Integer,Emp> fun02 = Emp::new;
Emp emp = fun02.apply(101);
System.out.println(emp);
}
数组引用
语法格式:数组类型::new
@Test
public void test16(){
//Lambda表达式实现
Function<Integer,String[]> fun01 = (x) -> new String[x];
String[] strs = fun01.apply(10);
System.out.println(strs.length); //输出结果: 10
//数组引用: 数组类型::new;
Function<Integer,String[]> fun02 = String[]::new;
String[] strs2 =fun02.apply(20);
System.out.println(strs2.length);//输出结果: 20
}