第三章(1) Lambda表达式

1.lambda表达式的基本形式

      在上一章中,你了解了利用行为参数化来传递代码有助于应对不断变化的需求。它允许你定义一个代码块来表示一个行为,然后传递它。你可以决定在某一事件发生时(例如单击一个按钮)或在算法中的某个特定时刻(例如筛选算法中类似于“重量超过150克的苹果”的谓词,或排序中的自定义比较操作)运行该代码块。一般来说,利用这个概念,你就可以编写更为灵活且可重复使用的代码了。但你也看到,使用匿名类来表示不同的行为并不令人满意:代码十分啰嗦,这会影响程序员在实践中使用行为参数化的积极性。在本章中,我们会教给你Java 8中解决这个问题的新工具——Lambda表达式。它可以让你很简洁地表示一个行为或传递代码。现在你可以把Lambda表达式看作匿名功能,它基本上就是没有声明名称的方法,但和匿名类一样,它也可以作为参数传递给一个方法。我们会展示如何构建Lambda,它的使用场合,以及如何利用它使代码更简洁。我们还会介绍一些新的东西,如类型推断和Java 8 API中重要的新接口。最后,我们将介绍方法引用(method ref-erence),这是一个常常和Lambda表达式联用的有用的新功能。

      可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。这个定义够大的,让我们慢慢道来。

1.匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!

2.函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。

3.传递——Lambda表达式可以作为参数传递给方法或存储在变量中。

4.简洁——无需像匿名类那样写很多模板代码。

     那么,咱们再通过一个实例,来再次看看这个lambda表达式,书接上文,我们现在要对第二章的猪按体重排一下序,那么在有lambda表达式之前,我们是这么做的:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Pig> pigList = new ArrayList<Pig>();
		pigList.add(new Pig("黑色", 120));
		pigList.add(new Pig("白色", 300));
		pigList.add(new Pig("黑色", 208));
		pigList.add(new Pig("白色", 188));
		Collections.sort(pigList, new Comparator<Pig>() {

			@Override
			public int compare(Pig o1, Pig o2) {
				// TODO Auto-generated method stub
				return o1.getWeight().compareTo(o2.getWeight());
			}
		});
		for(Pig pig : pigList) {
			System.out.println(pig.getWeight());
		}
   
	}

结果如下:

那么有了lambda表达式后呢,我们是这样的:

Collections.sort(pigList, (Pig a1, Pig a2) -> a1.getWeight().compareTo(a2.getWeight()));

当然,你也可以这样写,看你心情:

Comparator<Pig> byWeight = (Pig p1,Pig p2) -> p1.getWeight().compareTo(p2.getWeight());
Collections.sort(pigList, byWeight);

 暴露一下接口,方便清楚代码的来龙去脉和在IDE中的代码追踪,否则按第一种写法,你在ide中例如eclipse无法通过按住ctrl点击而找到接口。

我们刚刚给你展示的lambda有三个部分:

1.参数列表:这里它采用了Comparator中接口compare方法的参数,两个泛型。

2.箭头——箭头->把参数列表与Lambda主体分隔开。

3.Lambda主体——比较两个pig的重量。表达式就是Lambda的返回值了。

为了进一步说明,下面给出了Java 8中五个有效的Lambda表达式的例子:
 

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

2.(Apple a) -> a.getWeight() > 150 第二个Lambda表达式有一个Apple 类型的参数并返回一个boolean(苹果的重量是否超过150克)

3.(int x, int y) -> { System.out.println("Result:");

                     System.out.println(x+y); }
第三个Lambda表达式具有两个int类型的参数而没有返回值(void返回)。注意Lambda表达式可以包含多行语句,这里是两行。

4.() -> 42 第四个Lambda表达式没有参数,返回一个int。

5.(Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight()) 第五个Lambda表达式具有两个Apple类型的参数,返回一个int:比较两个Apple的重量

 lambda表达式的基本语法是:

(parameters参数) -> expression表达式

(parameters) -> { statements(语句); }

我们看到lambda表达式非常简单,下面我们做一个测试,看看你是否真正掌握lambda表达式:

请找出下列无效的lambda表达式:

1.()->{}

2.()->"fuck"

3.()->{return "fuck";}

4.(int i) ->return i+3

5.(String s)-> {"泥煤的";}

显然只有4和5是无效的。

(1)这个Lambda没有参数,并返回void。它类似于主体为空的方法:public void run() {}。

(2)这个Lambda没有参数,并返回String作为表达式。

(3)这个Lambda没有参数,并返回String(利用显式返回语句)。

(4)return是一个控制流语句。要使此Lambda有效,需要使花括号。

(5)“泥煤的”是一个表达式,不是一个语句。要使此Lambda有效,你可以去除花括号和分号,如下所示:(String s) -> "泥煤的"。或者如果你喜欢,可以使用显式返回语句,如下所示:(String s)->{return "泥煤的";}。

2.函数式接口

     我们已经很好的了解了lambda表达式,那么lambda在哪里使用呢?你可以在函数式接口中使用lambda表达式。那么什么叫做lambda表达式呢?譬如我们第二章关于猪的筛选条件接口

public interface PigPredicate {
 
	public Boolean test(Pig pig);
}

这就是一个函数式接口,你可以对其使用lambda实现之并将其作为参数传入其他方法使用其test方法对猪进行筛选。下面我们就来详细说明函数式接口。

      那么首先,什么是函数式接口呢?一言以蔽之,函数式接口就是只定义一个抽象方法的接口。你已经知道了Java API中的一些其他函数式接口,如我们在第2章中谈到的Comparator和Runnable。

      而我们的lambda表达式,就可以看作是一个函数式接口的实例!

     所谓只有一个抽象方法,并不是指你仅仅在接口中看到的那种接口里面只写了一个抽象方法,那就是函数接口,别忘了接口之间还有继承关系,如果一个接口继承了另一个接口,而他本身也拥有自己的抽象方法,那么它就不是函数式接口。

    那么有没有什么方法,能让我们在编写函数式接口的时候,能够避免这种错误呢?答案是有的,那就是通过@FunctionalInterface注解,这个注解能够帮助我们检测你编写的接口是不是函数式接口

3.函数描述符

     函数式接口的抽象方法的签名基本就是lambda的签名,我们管这种抽象方法叫做函数描述符。例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。通过上面的描述,我们知道你的lambda表达式必须符合这种接口的函数描述符才能有效。

4.JAVA8 api为我们提供的函数式接口

   Java 8的库设计师帮你在java.util.func-tion包中引入了几个新的函数式接口。我们接下来会介绍Predicate、Consumer和Function为了便于文章检索,我在这里把它放入下一章讲解。

猜你喜欢

转载自blog.csdn.net/qq564425/article/details/81283409