Java8实战-总结8

Java8实战-总结8

  • Lambda表达式
    • 在哪里以及如何使用Lambda
      • 函数式接口
      • 函数描述符

Lambda表达式

在哪里以及如何使用Lambda

在哪里可以使用Lambda表达式。在上一个例子中,把Lambda赋给了一个Comparator类型的变量。也可以在之前实现的filter方法中使用Lambda:

List<Apple> greenApples = filter(inventory, (Apple a) -> "green".equals(a.getcolor()));

到底在哪里可以使用Lambda呢?可以在函数式接口上使用Lambda表达式。在上面的代码中,可以把Lambda表达式作为第二个参数传给filter方法,因为它这里需要Predicate,而这是一个函数式接口。

函数式接口

在上一个例子中,为了参数化filter方法的行为而创建的Predicate接口,它就是一个函数式接口。因为Predicate仅仅定义了一个抽象方法:

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

一言以蔽之,函数式接口就是只定义一个抽象方法的接口。已经知道了Java API中的一些其他函数式接口,如ComparatorRunnable

//java.uti1.Comparator
public interface Comparator<T> {
	int compare(T ol,T o2);
}

//java.lang.Runnable
public interface Runnable {
	void run();
}

//java.awt.event.ActionListener
public interface ActionListener extends EventListener {
	void actionPerformed(ActionEvent e);
}

//java.util.concurrent.Callable
public interface Callable<V> {
	V call();
}

//java.security.PrivilegedAction
public interface PrivilegedAction<V> {
	V run();
}

注意,接口现在还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

测验:函数式接口
	下面哪些接口是函数式接口?
	public interface Adder {
		int add(int a, int b);
	}
	
	public interface SmartAdder extends Adder {
		int add(double a, double b);
	}
	
	public interface Nothing {
	}
	
答案:只有Adder是函数式接口。
SmartAdder不是函数式接口,因为它定义了两个叫作add的抽象方法(其中一个是从Adder那里继承来的)Nothing也不是函数式接口,因为它没有声明抽象方法。

用函数式接口可以干什么呢?Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后再直接内联将它实例化。下面的代码是有效的,因为Runnable是一个只定义了一个抽象方法run的函数式接口:

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

//使用匿名类
Runnable r2 = new Runnable() {
	public void run() {
	System.out.println("Hello World 2");
	}
};

public static void process(Runnable r) {
	r.run();
}
//打印“Hello World 1"
process(r1);

//打印“Hello World 2”
process(r2);

//利用直接传递的Lambda打印“Hello World 3”
process(() -> System.out.println("Hello World 3"));

函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。将这种抽象方法叫作函数描述符。例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)

在前面使用了一个特殊表示法来描述Lambda和函数式接口的签名。() -> void代表了参数列表为空,且返回void的函数。这正是Runnable接口所代表的。举另一个例子,(Apple, Apple )-> int代表接受两个Apple作为参数且返回int的函数。

]现在,只要知道Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法就好了,当然这个Lambda表达式的签名要和函数式接口的抽象方法一样。比如,在之前的例子里,可以像下面这样直接把一个Lambda传给process方法:

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

process(() -> System.out.println("This is awesome!!"));

此代码执行时将打印"This is awesome!!"。Lambda表达式() -> System.out.println("This is awesome!!")不接受参数且返回void。 这恰恰是Runnable接口中run方法的签名。

“为什么只有在需要函数式接口的时候才可以传递Lambda呢?”语言的设计者也考虑过其他办法,例如给Java添加函数类型。但是他们选择了现在这种方式,因为这种方式自然且能避免语言变得更复杂。此外,大多数Java程序员都已经熟悉了具有一个抽象方法的接口的理念(例如事件处理)。

测验:在哪里可以使用Lambda?

以下哪些是使用Lambda表达式的有效方式?
(1) execute(() -> {});

	public void execute(Runnable r) {
		r.run()
	};
	
(2)public Callable<String> fetch() {
		return () -> "Tricky example ;-)";
	}
(3)PredicatecApple> p = (Apple a) -> a.getWeight();

答案:只有12是有效的。

第一个例子有效,是因为Lambda()->{}具有签名() -> void,这和Runnable中的抽象方法run的签名相匹配。请注意,此代码运行后什么都不会做,因为Lambda是空的!

第二个例子也是有效的。事实上,fetch方法的返回类型是Callable<String>Callable<String>基本上就定义了一个方法,签名是() -> String,其中TString代替了。因为Lambda() -> "Trickyexample;-)"的签名是() -> String,所以在这个上下文中可以使用Lambda。

第三个例子无效,因为Lambda表达式(Apple a) -> a.getWeight()的签名是(Apple)->
Integer,这和Predicate<Apple>:(Apple) -> boolean中定义的test方法的签名不同。
@FunctionalInterface又是怎么回事?
如果去看看新的JavaAPI,会发现函数式接口带有@FunctionalInterface的标注。这个标注用于表示该接口会设计成一个函数式接口。如果用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。例如,错误消息可能是“Multiple non-overridingabstract methods found in interface Foo",表明存在多个抽象方法。请注意,@FunctionalInterface不是必需的,但对于为此设计的接口而言,使用它是比较好的做法。它就像是@Override标注表示方法被重写了。

你可能感兴趣的:(python,开发语言)