【JDK】聊聊Java 8的新特性之Lambda表达式

本文详细介绍了Java 8中的Lambda表达式,包括其作为匿名函数的概念、优化场景、基础语法和与函数式接口的关系。通过实例展示了Lambda如何简化代码,以及如何利用其进行条件查询优化。同时,文章还提到了Java内置的四大核心函数式接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 8的新特性之Lambda表达式

jdk1.8 自从其发布以来一直都是 Java 开发的一个主要版本,其新增了非常多的特性,支持函数式编程
而 Lambda 表达式则是 jdk1.8 的一个核心之一

1. Lambda表达式简介

Lambda 表达式是一个匿名函数,可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更加简洁、灵活的代码。作为一种更为紧凑的代码风格,使 Java 语言表达能力得到提升

语法糖:指使用更加f方便,但是原理不变的代码语法。例如在遍历集合时使用的 for-each 语法,其底层的实现原理仍然是迭代器。从应用层面上讲,Java 中d的 Lambda 表达式可以被当作匿名内部类的语法糖,但是二者在原理上是不同的。

以下是 Lambda 表达式的重要特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值

语法对比
匿名内部类demo

//原来的匿名内部类
Comparator<Integer> com = new Comparator<Integer>() {
	@Override
	public int compare(Integer o1, Integer o2) {
		// TODO Auto-generated method stub
		return Integer.compare(o1, o2);
	}
};
		
TreeSet<Integer> ts = new TreeSet<>(com);

使用 lambda 表达式改写

Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
TreeSet<Integer> ts = new TreeSet<>(com);

2. Lambda优化场景

需求,按按条件查询数据
如:获取学生中年龄大于 20 的信息
创建实体类Student

package com.jiker.bean;

public class Student {
	
	private Integer id;
	private String sName;
	private Integer age;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getsName() {
		return sName;
	}
	public void setsName(String sName) {
		this.sName = sName;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", sName=" + sName + ", age=" + age + "]";
	}
		public Student(Integer id, String sName, Integer age) {
		super();
		this.id = id;
		this.sName = sName;
		this.age = age;
	}
	
}

由于本次实验不涉及数据库的操作,可以将学生信息存储在一个 List 中

java.util.List<Student> list = Arrays.asList(
			new Student(1, "zhangsan", 19),
			new Student(2, "lisi", 22),
			new Student(3, "wangwu", 20),
			new Student(4, "zhaoliu", 17),
			new Student(5, "liuqi", 21)
		);

1、原始方法

public java.util.List<Student> filterStudent(java.util.List<Student> list){
	java.util.List<Student> stu = new ArrayList<>();
		
	for (Student student : list) {
		if (student.getAge() > 20) {
			stu.add(student);
		}
	}
	return stu;
}

若此时,还有一个需求即查找 id 大于 2 的学生信息,则代码为

public java.util.List<Student> filterStudent2(java.util.List<Student> list){
	java.util.List<Student> stu = new ArrayList<>();
		
	for (Student student : list) {
		if (student.getId() > 20) {
			stu.add(student);
		}
	}
	return stu;
}

可以看到,两者仅有 if 判断条件中的语句不一样
此时,若项目中有多种类似需求时,则会出现大量的冗余代码

2、优化方式1:定义接口(策略设计模式)

//接口定义
package com.jiker.predicate;

public interface MyPredicate<T> {
	public boolean test(T t);
}

实现类过滤器
package com.jiker.predicate;

import com.jiker.bean.Student;

public class FilterStudentByAge implements MyPredicate<Student> {

	@Override
	public boolean test(Student student) {
		// TODO Auto-generated method stub
		return student.getAge() > 20;
	}

}
public List<Student> filerStud(List<Student> list,MyPredicate<Student> mp){
	List<Student> students = new ArrayList<>();
	for (Student student : list) {
		if (mp.test(student)) {
			students.add(student);
		}
	}
	return students;
}

//测试
List<Student> students =  filerStud(list, new FilterStudentByAge());
for (Student student : students) {
	System.out.println(student);
}

不足:每次实现一个新的需求时,都需要新创建一个 MyPredicate 的实现类

3、优化方式2:匿名内部类

List<Student> students = filerStud(list, new MyPredicate<Student>() {

	@Override
	public boolean test(Student t) {
		// TODO Auto-generated method stub
		return t.getAge() > 20;
	}
});
for (Student student : students) {
	System.out.println(student);
}

4、优化方式3:Lambda 表达式

//Lambda表达式
List<Student> students = filerStud(list, (e) ->e.getAge() > 20);
list.forEach(System.out::println);

3. Lambda表达式基础语法

Java 8 中引入了一个新的操作符:"->",该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能,即: Lambda 体

语法格式一:无参数、无返回值

() -> System.out.println("无返回值");

如:

//原始方式
Runnable r = new Runnable(){
	@Override
	public void run(){
		System.out.print("run ...");
	}
}
r.run();

//改写
Runnable r = () -> System.out.print("run ...");
r.run();

注意事项:若在局部内部类中应用了一个同级别的变量,在 JDK1.7 中,该变量需要是 final 类型
而在 JDK1.8 中,不需要定义变量为 final 类型,但仍然不能修改变量的值,同时 Lambda 表达式也得遵守这一规则

int num = 0;
Runnable r = () -> System.out.print("run ..." + num); //编译通过

Runnable r = () -> System.out.print("run ..." + num++); //编译报错

语法格式二:有参数,无返回值
若只有一个参数,小括号可以不写

Consumer<String> con = (x) -> System.out.print(X);//推荐使用
Consumer<String> con = x -> System.out.print(X);

con.accept("Lambda语法")

语法格式三:有参数,有返回值
若 Lambda 体中有多条语句,则需要使用大括号
若仅有一条返回语句,则大括号和 return 关键字都可省略

Comparator<Integer> com = (x,y) -> {
	System.out.println("使用大括号");
	return Integer.compare(x,y); 
}

语法格式四:Lambda 表达式的数据类型可以省略不写
因为 JVM 的编译器可以通过上下文推断出数据类型,称为 “类型推断”
相同原理:

String[] str = {"a","b","c"};

List<String> list = new ArrayList<>(); //JDK1.7

//JDK1.8
public void show(Map<String,Integer> map){}
show(new HashMap<>());
... ...

4. Lambda表达式与函数式接口

函数式接口 (Functional Interface) 也是 Java 8 的另一个新特性
函数式接口:在接口中有且只有一个抽象方法,但可以有多个非抽象方法的接口(默认、静态、私有)
函数式接口,即适用于函数式编程场景的接口。而 Java 中的函数式编程体现就是 Lambda,所以函数式接口就是可以适用于 Lambda 使用的接口。只有确保接口中有且仅有一个抽象方法,Java 中的 Lambda 表达式才能顺利地进行推导

格式:
只要确保接口中有且仅有一个抽象方法即可

修饰符 interface 接口名称{
	public abstract 返回值类型 方法名称(可选参数信息)l

	//其他非抽象方法内容
}

由于接口中抽象方法的 public abstract 是可以省略的,所以定义一个函数式接口非常简单

public interface MyFunctionalInterface{
	void myMethod();
}

同时,可以使用注解 @FunctionalInterface 加在某一接口上,检查该接口是否为一个函数式接口

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 * /

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
Java 内置四大核心函数式接口
函数式接口参数类型返回类型用途
Consumer< T> 消费型接口Tvoid对类型为 T 的对象应用操作,包含方法 void accept(T t)
Supplier< T> 供给型接口T返回类型为 T 的对象,包含方法 T get()
Function< T,R> 函数型接口TR对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象,包含方法 R apply(T t)
Predicate< T> 断定型接口Tboolean确定类型为 T 的对象是否满足某约束,并返回 boolean 值,包含方法 boolean testt(T t)

Example1:消费型接口

@Test
public void test1(){
	cons(1000,(m) -> System.out.println("消费 " + m));
}

public void cons(Integer money,Consumer<Integer> con){
	con.accept(money)
}

Example2:供给型接口

//产生一些整数,放入集合
public List<Integer> getList(int num Supplier<Integer> sup){
	List<Integer> list = new ArrayList<>();
	for(int i = 0;i < num;i++){
		Interger n = sup.get();
		list.add(n);
	}
	return list;
}

@Test
public void test2(){
    List<Integer> list = getList(10,() -> (int)(Math.random() * 100));
}

Example3:函数型接口

//处理字符串
public String strHandler(String str,Function<String,String> fun){
	return fun.apply(str);
}

@Test
public void test3(){
	String str = strHandler("\t\t\t  Lambda ",(str) -> str.trim);
}

Example4:断定型接口

//将满足条件的字符串f放入集合
public List<String> filterStr(List<String> list,Predicate<String> pre){
	List<String> strList = new ArrayList<>();
	for(String str : list){
		strList.add(str);
	}
	return strList;
}

@Test
public void test4(){
	List<String> list = Arrays,asList("Hello","Jiker","Lambda");
	List<String> list2 = filterStr(list,(s) -> s.length() > 3);
}

时间:2019.6.28 19:27

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值