JAVA中的Lambda表达式

Lambda表达式简介

Lambda表达式用于将代码块作为参数传入方法中,有的时候,在你创建某个方法的时候,你并不知道方法应该对参数进行怎样的操作,只有到了使用的时候,才能真正确定。这种情境下,就可以借助接口来实现,具体可以使用接口的实现类、匿名内部类和Lambda表达式来实现:

//Command接口用以封装方法的”处理行为“
interface Command{
//	process代表了处理行为,具体处理行为是怎样的,现在无法确定,需要通过实现类、匿名内部类或Lambda表达式三种方式实现
	void process(int[] target);
}
//ProcessArray类用接口中的处理方法来处理数组
class ProcessArray{
	public void process(int[] target,Command cmd) {
		cmd.process(target);
	}
}

Command接口中封装了一个process方法,用以处理数据,在定义该接口时,还未确定应该对数据进行怎样的处理。
ProcessArray类中封装了一个process方法,这个方法调用Command接口中的process方法来处理数据。以下通过三种方式来实现将代码块作为参数传入方法:

借助接口的实现类实现:

class AddCommand implements Command{
	public void process(int[] target) {
		int sum = 0;
		for(int tmp : target) {
			sum += tmp;
		}
		System.out.println("数组元素总和为:" + sum);
	}
}
public class CommandTest {
	public static void main(String[] args) {
		ProcessArray pa = new ProcessArray();
		int[] target = {1,2,3,4};
		pa.process(target, new AddCommand());	
	}
}

借助匿名内部类实现

public class CommandTest {
	public static void main(String[] args) {
		ProcessArray pa = new ProcessArray();
		int[] target = {1,2,3,4};
		pa.process(target, new Command() {
			public void process(int[] target) {
				int sum = 0;
				for(int tmp:target) {
					sum += tmp;
				}
				System.out.println("数组元素总和为:"+sum);
			}
		});	
	}
}

借助Lambda表达式实现:

public class CommandTest {
	public static void main(String[] args) {
		ProcessArray pa = new ProcessArray();
		int[] target = {1,2,3,4};
		pa.process(target, (int[] targetProcess)->{
			int sum = 0;
			for(int tmp : targetProcess) {
				sum += tmp;
			}
		});
	}
}

相比于其他两种方式,借助Lambda表达式更为简洁。
Lambda表达式由三部分组成:

  1. 形参列表:Lambda表达式中,允许省略形参类型,如果只有一个形参,可以直接省略形参。
  2. 箭头:->,英文字符
  3. 代码块:若代码块只包含一条语句,则可以省略代码块的{}。

具体如下:

interface Eatable{
	void taste();
}
interface Flyable{
	void fly(String weather);
}
interface Addable{
	int add(int a,int b);
}
public class LambdaQs {
	public void eat(Eatable e) {
		System.out.println("Lambda表达式被当作Eatable接口对象");
		e.taste();	
	}
	public void drive(Flyable f) {
		System.out.println("Lambda表达式被当作Flyable接口对象");
		f.fly("晴天");	
	}
	public void test(Addable add) {
		System.out.println("Lambda表达式被当作Addable接口对象");
		System.out.println("1+1="+add.add(1, 1));
	}
	public static void main(String[] args) {
		LambdaQs lq = new LambdaQs();
		//Lambda表达式只有一个形参,一条语句,可以省略形参,省略形参的(),省略{}
		lq.eat(()->System.out.println("牛肉吃起来不错"));
		lq.drive(weather ->
		{
			System.out.println("今天的天气是"+weather);
			System.out.println("飞机飞行平稳");
		});
		lq.test((a,b)->a+b);
	}
}

上述代码的输出结果为:

Lambda表达式被当作Eatable接口对象
牛肉吃起来不错
Lambda表达式被当作Flyable接口对象
今天的天气是晴天
飞机飞行平稳
Lambda表达式被当作Addable接口对象
1+1=2

可见,Lambda表达式到底会被当作何种类型对象,取决于运行环境。

Lambda表达式与函数式接口

函数式接口就是只包含一个抽象方法的接口,那么函数式接口和Lambda有什么联系呢?请看:

public class lambdaTest {
	//下方代码运行正常
	Runnable r = () ->{
		System.out.println("成功执行Lambda表达式");
	};
	//下方代码报错
	Object obj = ()->{
		System.out.println("成功执行Lambda表达式");
	};
}

Runnable是JAVA自带的函数式接口,Object不是函数式接口,而是所有类的父类。为什么一个正常运行?一个报错呢?

Lambda表达式本身没有方法名,所以Lambda表达式的目标类型只能是明确的函数式接口,因为如果一个接口包含多个方法的话,程序就不知道Lambda表达式到底执行的是哪个方法了,所以会报错。

综上,Lambda表达式本质上就是使用更简洁的语法来创建函数式接口的实例,相比于实现类、匿名内部类更为简洁方便。

要保证Lambda表达式的目标类型是明确的函数式接口,可以借助以下三种方法:

  1. 将Lambda表达式赋值给函数式接口类型的变量
  2. 将Lambda表达式作为函数式接口类型的变量,传递给某个方法
  3. 对Lambda表达式进行强制类型转换,例如上述报错代码可修改为:
	Object obj = (Runnable)()->{
		System.out.println("成功执行Lambda表达式");
	};

在java.util.function包中预定义了大量函数式接口,大致包括以下四类:

  1. XxxFunction:包含apply()方法,通常用于对数据进行转换处理
  2. XxxConsumer:包含accept()方法,与apply()方法类似,但不会返回处理结果
  3. XxxPredicate:包含test()方法,判断参数是否满足某种条件,返回boolean值
  4. XxxSupplier:包含getAsXxx()方法,根据某种逻辑算法,返回一个数据

Lambda表达式中的方法引用与构造器引用

如果Lambda表达式的代码块只有一条代码,就可以在代码块中使用方法引用和构造器引用,是得Lambda表达式更为简洁,常见有四种引用方式:引用类方法、引用特定对象的实例方法、引用某类对象的实例方法、引用构造器,具体如下:

1.引用类方法,代码如下:

interface Converter{
	Integer convert(String a);
}
public class lambdaTest {
	public static void main(String[] args) {
		Converter con = a -> Integer.valueOf(a);
		int b = con.convert("71");
		//下方代码输出71
		System.out.println(b);
	}
}

上方代码中,定义了函数式接口converter用以转换数据类型,在main函数中,借助Lambda表达式来实现接口。该Lambda表达式仅包含一行代码,可以省略花括号,并且可以引用类方法,也就是Integer类的valueof方法。可见,最终把字符串转化为了整数。调用代码可以进一步简化:

Converter con = Integer::valueOf;

2.引用特定对象的实例方法,代码如下:

interface Converter{
	Integer convert(String a);
}
public class lambdaTest {
	public static void main(String[] args) {
		String a = "The Lakers are the championship";
		Converter con = from -> a.indexOf(from);
		int b = con.convert("L");
		//下方输出4,因为L在a字符串中的索引是4
		System.out.println(b);
	}
}

与1中不同的是,这里Lambda表达式调用的并非类方法,而是对象的实例方法,也就是代码中的a的indexof方法。那么在后面调用接口对象的indexof方法时,就会相应的去a字符串中的对应字母的索引。类似的,调用代码可进一步简写为:

Converter con = a::indexOf(from);

3.引用某类对象的实例方法,代码如下:

interface MyTest{
	String test(String a, int b, int c);
}
public class lambdaTest {
	public static void main(String[] args) {
		MyTest mt = (a,b,c) -> a.substring(b,c);
		String str = mt.test("The Lakers are the championship", 2, 8);
		System.out.println(str);
	}
}

与2中不同,2是引用特定对象,3是引用某类对象,具体对象需要你在引用的时候再指定。类似的,调用代码可进一步简写为:

MyTest mt = String::substring;

此时,函数式接口中,被实现方法的第一参数作为调用者,后面的全部参数传递给该方法作为方法参数。

4.引用构造器,代码如下

class lambdaClass{
	public lambdaClass(String a) {
		System.out.println("构造器中参数为"+a);
	}
}
public class lambdaTest {
	public static void main(String[] args) {
		MyTest mt = (String a) -> new lambdaClass(a);
		//下方代码输出:构造器中参数为a
		mt.test("a");
	}
}

上方lambda表达式中,引用的就是lambdaClass类的构造器。可简化为:

MyTest mt = lambdaClass :: new;

Lambda表达式和匿名内部类

可见,lambda表达式可以视作匿名内部类的简化,但是二者存在区别,如下:

  1. lambda表达式只能为函数式接口创建实例,但匿名内部类无此限制
  2. 匿名内部类可以为抽象类或普通类创建实例,但lambda表达式不可以
  3. 匿名内部类可以调用接口中定义的默认方法,但lambda表达式不可以
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值