第二篇 Lambda表达式

本文深入介绍了Java 8的Lambda表达式,它简化了代码,使得匿名函数传递更加便捷。Lambda由参数列表、箭头和函数主体组成,可作为参数传递给方法。文中通过比较传统实现与Lambda的差异,展示了Lambda在创建Comparator、Runnable等接口实例时的简洁性。此外,文章还讨论了函数式接口、函数描述符的概念,并以环绕执行模式为例,演示了如何利用Lambda处理资源,如文件读取。
摘要由CSDN通过智能技术生成

Lambda表达式可以让我们简洁的表示一个行为或是传递代码。现在我们可以把Lambda表达式看作匿名功能,他基本上就是没有声明名称的方法,但和匿名类一样,它也可以作为参数传递给一个方法。我们会展示如何构建Lambda,他的使用场所,以及如何利用它使代码更简洁。我们还会介绍一些新的东西,如类型推断和Java8API中重要的新接口。我们还会介绍方法引用,这是一个常常和Lambda表达式联用的有用的新功能。

 2.1.Lambda介绍

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:他没有名称,但是有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

  • 匿名——这里的匿名说的是它不像普通方法那样有一个明确的方法名。
  • 函数——Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类,还可能有一个可以抛出的异常列表。
  • 传递——Lambda可以作为参数传递给方法或存储在变量中。
  • 简洁——无需像匿名类那样写很多模板代码。

比如利用Lambda表达式,可以更简洁的自定义一个Comparator对象:

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

之前创建Comparator对象需要这样:

Comparator<Apple> byWeight = new Comparator<Apple>() {
	@Override
	public int compare(Apple a1, Apple a2) {
		return a1.getWeight().compareTo(a2.getWeight());
	}
};

现在,请注意使用Lambda表达式时我们基本上只是传递了比较苹果重量所真正需要的代码。看起来就像是只传递了compare方法的主体。一般的Lambda表达式有三个部分组成:

  • 参数列表——这里它采用了Comparator中的compare方法的两个参数。
  • 箭头——把参数列表和Lambda主体分隔开。
  • Lambda主体——比较两个Apple的重量。表达式的返回值就是Lambda的返回值。

为了进一步说明Lambda,我们给出几个例子:

//具有一个String类型的参数并返回一个int。Lambda没有return语句,因为已经隐含了return
(String s)->s.length()

//Lambda表达式有一个Apple类型的参数并返回一个boolean
(Apple a)->a.getWeight>150

//Lambda表达式有两个int类型的参数而没有返回值。注意Lambda表达式可以包含多行语句,这里有两行
(int x,int y)->{
  	System.out.println("Result:");
	System.out.println(x + y);
}

//Lambda表达式没有参数,返回一个int
()->42

//Lambda表达式有两个Apple类型的参数,返回一个int
(Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight())

 Lambda表达式的基本语法是:
(parameters)-> expression 或是(parameters)-> { statements }

使用案例Lambda示例
布尔表达式(List<String> list)->list.isEmpty()
创建对象()->new Apple(10)
消费一个对象(Apple a)->{ System.out.println(a.getWeight()); }
从一个对象中选择/抽取(String s)->s.length()
组合两个值(int a,int b)->a*b
比较两个对象(Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight())

2.2.在哪里以及如何使用Lambda 

可以在函数式接口上使用Lambda表达式。接下来我们就来详细解释这是什么意思,以及函数式接口是什么。

2.2.1.函数式接口

函数式接口就是只定义一个抽象方法的接口,比如下面的一些接口就是JavaAPI中的一些函数式接口:

public interface Comparator<T> {
    int compare(T o1, T o2);
}
public interface Runnable {    
   public abstract void run();
}
public interface ActionListener extends EventListener {
 public void actionPerformed(ActionEvent e);
}
public interface Callable<V> {
    V call() throws Exception;
}
public interface PrivilegedAction<T> {
    T run();
}

现在接口可以拥有默认方法。即使有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

Lambda表达式允许我们直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体来说,是函数式接口一个具体实现的实例)。你用匿名内部类也可以完成同样的事情,只不过很笨拙:需要提供一个实现,然后再直接内联将它实例化。

public class Demo{

	public static void main(String[] args) {

        Runnable r1 = () -> System.out.println("Hello World 1");

		Runnable r2 = new Runnable() {
			@Override
			public void run() {
				System.out.println("Hello World 2");
			}
		};
		process(r1);
		process(r2);
		process(() -> System.out.println("Hello World 3"));
     }

	public static void process(Runnable r) {
		r.run();
	}
}

2.2.2.函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫做函数描述符。例如,Runnable接口可以看作一个什么也不接受什么也不返回的函数的签名,因为它只用一个叫做run的抽象方法,这个方法什么也不接受,什么也不返回。我们使用类似下面特殊的方式来描述函数式接口和Lambda表达式的签名:

  • ()->void  代表了参数列表为空,且返回void的函数
  • (Apple,Apple)->int 代表接受两个Apple作为参数且返回int的函数

2.3.把Lambda付诸实践:环绕执行模式

比如资源处理(文件处理或是数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很相似,并且会围绕着执行处理的那些总要代码。这就是所谓的环绕执行模式:

2.3.1.第1步:记得行为参数化

public static String processFile() throws IOException {
	try (BufferedReader br = new BufferedReader(new FileReader("Data.txt"))) {
		return br.readLine();
	}
}

上面这段代码是有局限性的,你只能读取文件的第一行。如果你想要返回头两行或是返回使用频率最高的词汇,该怎么办了?理想情况下你需要重新执行设置和清理的代码,并告诉processFile方法对文件执行不同的操作,这个听起来是不是很耳熟?是的,你需要把processFile方法的行为参数化。你需要一种方法把行为传递给processFile,以便它可以利用BufferedReader执行不同的行为。基本上来讲,你需要的一个接收BufferedReader并返回String的Lambda。例如,下面就是从BufferedReader中打印两行的写法:

String result =processFile((BufferedReader br)->br.readLine()+br.readLine());

 2.3.2.第2步:使用函数式接口传递行为

Lambda表达式仅可用于上下文是函数式接口的情况,我们需要创建一个能匹配(BufferedReader)->String,还可以抛出IOException异常的接口:

@FunctionalInterface
public interface BufferedReaderProcessor {
	String process(BufferedReader br) throws IOException;
}

现在可以把这个接口作为新的processFile方法的参数了:

public static String processFile(BufferedReaderProcessor p) throws IOException {
	return null;
}

2.3.3.第3步:执行一个行为

任何(BufferedReader)->String形式的Lambda表达式都可以作为参数来传递,因为他们符合BufferedReaderProcessor接口中定义的process方法签名。现在我们只需要一种方法在processFile方法主体内执行Lambda所代表的代码。Lambda表达式允许直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例,因此,你可以在processFile方法主体内,对得到的BufferedReaderProcessor对象调用process方法执行处理:

public static String processFile(BufferedReaderProcessor p) throws IOException {
	try (BufferedReader br = new BufferedReader(new FileReader("Data.txt"))) {
		return p.process(br);//处理BufferedReader
	}
}

2.3.4.传递Lambda

现在可以传递不同的Lambda重用processFile方法,并以不同的方式处理文件了:
 

//处理一行
String oneLine = processFile((BufferedReader b) -> b.readLine());

//处理两行
String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豢龙先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值