JDK8新特性 - Lambda表达式、内置函数式接口、方法引用及构造器引用

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

Lambda是简化代码的一种方式,但其只能用在函数式接口。函数式接口就是接口中只有一个方法吗,可以使用注解@FunctionalInterface标注修饰。

Lambda表达式

一、无参数无返回值情况

通过Runable的接口演示匿名内部类和Lambda表达式。

@Test
public void test1() {
	// 演示匿名内部类的方式
	Runnable r1 = new Runnable() {
		@Override
		public void run() {
			System.out.println("匿名内部类:r1");
		}
	};
	// 使用Lambda表达式简化
	Runnable r2 = () -> System.out.println("匿名内部类:r2");
	// 调用
	r1.run();
	r2.run();
}
二、有一个参数且无返回值
表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即类型推断

1、创建一个函数式接口

public interface Lambda<T> {
	void accept(T t);
}

2、测试

@Test
public void test2() {
	Lambda<String> str = x -> System.out.println(x);
	str.accept("一个参数可以省略包裹参数的括号!");
}
三、多个参数且Lambda体含多条语句,并且含有返回值

1、创建一个函数式接口

public interface Lambda<T> {
	Integer accept(T t1, T t2);
}

2、测试

@Test
public void test3() {
    // 无需写参数类型,
	Lambda<Integer> num = (x,y) -> {
		System.out.println("2个参数,2行语句,1个返回值!");
		return x - y;
	};
	System.out.println(num.accept(3, 2));;
}

四大内置函数式接口

只包含一个抽象方法的接口,称为函数式接口。
可以通过 Lambda 表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在任意函数式接口上使用@FunctionalInterfaceFunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在这里插入图片描述

一、消费型接口 - Comsumer
Consumer<T> : 消费型接口
		void accept(T t);

即无参数,无返回值。
Comsumer<T>是消费型接口,其方法为void accept(T t)

public void doSomething(String something, Consumer<String> com) {
	com.accept(something);
}

@Test
public void testComsumer() {
    // 将接收的参数拿来消费
	doSomething("玩LOL!",x -> System.out.println("LCY正准备打开电脑" + x));
}
二、供给型接口 - Supplier
 Supplier<T> : 供给型接口
 		T get(); 

即无参数,但有返回值。

public List<Integer> doSomething(int num, Supplier<Integer> sup) {
	// 将sup提供的数字加入到集合
	List<Integer> list = new ArrayList<Integer>();
	for(int i = 0; i < num; i++) {
		Integer n = sup.get();
		list.add(n);
	}
	return list;
}

@Test
public void testSupplier() {
	List<Integer> list = doSomething(5, () -> (int)(Math.random() * 10));
	for(int i = 0; i < list.size(); i++) {
		System.out.print(list.get(i) + " ");
	}
}
三、函数型接口

即有参数,且有返回值。

public String doSomething(String something,Function<String, String> func) {
	return func.apply(something);
}

@Test
public void testFunction() {
	// 获取字符串的某个字符串
	String str1 = doSomething("打游戏好吗?", (str) -> str.charAt(3) + "");
	System.out.println(str1);
}
四、断言型接口

即有参数,返回boolean型结果。

public List<String> doSomething(List<String> list, Predicate<String> pre){
	List<String> strList = new ArrayList<>();
	for(String str : list) {
		// 符合条件的才加入strList集合
		if(pre.test(str)) {
			strList.add(str);
		}
	}
	return strList;
}

@Test
public void testPredicate() {
	List<String> list = Arrays.asList("LCY","JYQC","XBYX","QNWB","ZW");
	// 字符串长度大于3的符合条件
	List<String> strList = doSomething(list, x -> x.length() > 3);
	strList.forEach(System.out::println);
}
其他接口

在这里插入图片描述

方法引用

Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用,即Lambda的另外一种表现形式。
方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致。

一、对象引用::实例方法名
@Test
public void testT1() {
	// 创建一个User类,属性为name和age
	User user = new User("LCY", 22);
	// Lambda表达式
	Supplier<String> sup1 = () -> user.getName();
	System.out.println(sup1.get());
	// 方法引用方式
	Supplier<Integer> sup2 = user::getAge;
	System.out.println(sup2.get());
}
二、类名::静态方法名
@Test 
public void testT2() {
	Comparator<Integer> com1 = (x,y) -> Integer.compare(x, y);
	System.out.println(com1.compare(1, 2));
	Comparator<Integer> com2 = Integer::compare;
	System.out.println(com2.compare(7, 6));
}
三、类名::实例方法名

若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:ClassName::MethodName。

@Test
public void testT3() {
	BiPredicate<String, String> bp1 = (x,y) -> x.equals(y);
	System.out.println(bp1.test("abcd", "dcba"));
	// 参数1是调用equals的,参数2是equals方法的参数
	// 因此可以使用类名::方法名
	BiPredicate<String, String> bp2 = String::equals;
	System.out.println(bp2.test("123", "123"));
}
四、构造器引用

引用的哪个构造方法,取决于引用它的接口里的那个方法的参数列表,被引用的构造方法参数列表与其一致。

1、创建User类

public class User {
	private String name;
	private Integer age;
	
	public User() {
		this("默认昵称");
	}
	
	public User(String name) {
		this(name,111);
	}
	
	public User(Integer age) {
		this("默认昵称",age);
	}
	
	public User(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}

2、测试

public void testT4() {
	Supplier<User> sup1 = () -> new User();
	System.out.println(sup1.get());
	// 调用的构造方法与接口里的方法参数列表一致
	Supplier<User> sup2 = User::new;
	System.out.println(sup2.get());
	
	// 带一个参数的
	Function<String,User> fun1 = (name) -> new User(name);
	System.out.println(fun1.apply("LCY"));
	
	Function<Integer, User> fun2 = User::new;
	System.out.println(fun2.apply(123));
}
五、数组引用
类型[]::new
@Test
public void testT5() {
	Function<Integer, String[]> fun1 = (args) -> new String[args];
	String []strs = fun1.apply(10);
	System.out.println(strs.length);
	
	Function<Integer, User[]> fun2 = User[]::new;
	User []users = fun2.apply(5);
	System.out.println(users.length);
}

总结

Lambda表达式在某些场景用起来很方便,比如Swing中的某些事件接口实现方法,又比如常见的System.out::println打印。但是通过上面的部分例子,不难看出Lambda表达式在很多情况下是破坏了代码可读性,就比如说上面的类名::实例方法名,确实有点绕,这种情况就大大降低代码可读性,导致效率降低。
如果不是必须,我个人觉得还是少用比较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值