阿男的小窝

View the Project on GitHub

Lambda Expression的表达形式

这篇文章继续和大家聊聊Java 8里面的Lambda表达式。

下面这段代码展示了Lambda表达式里面几种要素:

import java.util.ArrayList;
import java.util.List;

public class PlayWithCollections {

	public static void main(String[] args) {
		List<String> list = new ArrayList();
		list.add("x");
		list.add("y");
		list.add("z");

		list.forEach(System.out::println);

		list.forEach(str -> {
			String out = str.toUpperCase();
			System.out.println(out);
		});
	}
}

上面的代码里,使用了ListforEach()方法。下面的代码是ListforEach()方法的实现代码:

@Override
public void forEach(Consumer<? super E> action) {
	Objects.requireNonNull(action);
	final int expectedModCount = modCount;
	@SuppressWarnings("unchecked")
	final E[] elementData = (E[]) this.elementData;
	final int size = this.size;
	for (int i=0; modCount == expectedModCount && i < size; i++) {
		action.accept(elementData[i]);
	}
	if (modCount != expectedModCount) {
		throw new ConcurrentModificationException();
	}
}

可以看到,这个方法接受一个Consumer类型的参数action,然后遍历List自身内部的元素elementData,把这些元素逐一传递给actionaccept()方法。

可以看出Consumer是一个functional interface,就是只定义了单一方法的interface。下面是Java标准库中Consumer接口的代码:

package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

	/**
	 * Performs this operation on the given argument.
	 *
	 * @param t the input argument
	 */
	void accept(T t);

	/**
	 * Returns a composed {@code Consumer} that performs, in sequence, this
	 * operation followed by the {@code after} operation. If performing either
	 * operation throws an exception, it is relayed to the caller of the
	 * composed operation.  If performing this operation throws an exception,
	 * the {@code after} operation will not be performed.
	 *
	 * @param after the operation to perform after this operation
	 * @return a composed {@code Consumer} that performs in sequence this
	 * operation followed by the {@code after} operation
	 * @throws NullPointerException if {@code after} is null
	 */
	default Consumer<T> andThen(Consumer<? super T> after) {
		Objects.requireNonNull(after);
		return (T t) -> { accept(t); after.accept(t); };
	}
}

上面的代码里,不需要关心andThen()方法,可以看到它标记了default属性。这个是Java 1.8版本中新加入的语言特性,可以允许直接在interface里面实现这个方法1

在这个Consumer里面,Lambda expression会作为参数传入到accept(T t)方法当中。

回到我们自己的代码,可以把一个函数传入:

list.forEach(System.out::println);

因为println(String x)方法满足Consumeraccept(T t)接口定义:

public void println(String x) {
	synchronized (this) {
		print(x);
		newLine();
	}
}

所以可以用上面的双引号的形式直接传入。我们也可以定义自己的逻辑:

list.forEach(str -> {
	String out = str.toUpperCase();
	System.out.println(out);
});

如上所示,我们构造了一个接受单一参数str的匿名函数,这个函数的逻辑直接写在了大括号里面。这样Java编译器会创建一个Consumer的anonymous class的实现,并且用上面这个函数的逻辑作为accept(String t)方法的实现。

  1. 关于default method,可以参考Oracle的文档说明:https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html