Java 8

革命即将来临

Halo9Pan / @Halo9Pan

转场样式

Cube - Page - Concave - Zoom - Linear - Fade - None - Default

主题

Default - Sky - Beige - Simple - Serif - Night
Moon - Solarized

Agenda

  • Functional interfaces
  • Lambda
  • Interface method
  • Stream API
  • Nashorn JavaScript
  • Time API
  • Collections API
  • Concurrency API
  • IO/NIO API

函数式接口

核心概念

如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口

java.lang.Runnable就是一个函数式接口,因为只有一个一个抽象方法

@FunctionalInterface

一个新的Annotation

把它放在一个接口前,表示这个接口是一个函数式接口

加上它的接口不会被编译,除非设法把它变成一个函数式接口

有点像@Override,都是声明了一种使用意图,避免把它用错

函数式接口


@FunctionalInterface
public interface Action {
	void run(String param);
	default void stop(String param) {
	}
}
						

Lambda

函数式编程的融合

Hello lambda!


Runnable r = () -> System.out.println("Hello lambda!");
						

新旧对比

	static Comparator futureComparator() {
		Comparator cmp = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);
		return cmp;
	}
						
	static Comparator classicComparator() {
		Comparator cmp = new Comparator() {
			@Override
			public int compare(Integer x, Integer y) {
				return (x < y) ? -1 : ((x > y) ? 1 : 0);
			}
		};
		return cmp;
	}
						

更多的写法


public class Executor {
	@FunctionalInterface
	static interface Action {
		void run(String s);
	}
	void futureExecute(Action action) {
		action.run("Hello, lambda!");
	}
	void classicExecute() {
		(new Action() {
			public void run(String param) {
				System.out.println(param);
			}
		}).run("Goodbye, ugly old code style.");
	}
	public static void main(String[] args) {
		Executor executor = new Executor();
		executor.classicExecute();
		executor.futureExecute((String param) -> System.out.println(param));
		executor.futureExecute(param -> System.out.println(param));
		executor.futureExecute(System.out::println);

		executor.futureExecute(s -> System.out.println("*" + s + "*"));
	}
}
						

捕获和非捕获

当Lambda表达式访问一个定义在Lambda表达式体外的非静态变量或者对象时,这个Lambda表达式称为“捕获的”


int x = 5; return y -> x + y;
						

为了保证这个lambda表达式声明是正确的,被它捕获的变量必须是“有效final”的

所以要么它们需要用final修饰符号标记,要么保证它们在赋值后不能被改变

非捕获的lambda只需要计算一次. 然后每次使用到它都会返回一个唯一的实例

捕获的lambda表达式每次使用时都需要重新计算一次,从目前实现来看,很像实例化一个匿名内部类的实例

接口方法


public interface IAction {
	void run();
	default void init(String param) {
		System.out.println(param);
	}
}
						

实现


public class ActionOne implements IAction {
	public ActionOne() {
	}
	@Override
	public void init(String param) {
		System.out.println("implement one method");
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
	}
}
						

Object?


public interface IWrong {
	void run();
	default void init(String param) {
		System.out.println(param);
	}
	default int hashCode() { // Error
	}
}
						

Stream API

流能够是 串行的 或者 并行的

可以使用其中一种方式开始,然后切换到另外的一种方式

使用stream.sequential()或stream.parallel()来达到这种切换

串行流在一个线程上连续操作

并行流就可能一次出现在多个线程上

Code


		List<Person> list = persons.stream().collect(Collectors.toList());
						

		List<Person> filterList = persons.stream().filter(p -> p.name == "Halo").collect(Collectors.toList());
						

		List<Person> list = persons.stream().parallel().collect(Collectors.toList());
						

中间的操作

filter 排除所有与断言不匹配的元素

map 通过Function对元素执行一对一的转换

flatMap 通过FlatMapper将每个元素转变为无或更多的元素

peek 对每个遇到的元素执行一些操作,主要对调试很有用

distinct 根据.equals行为排除所有重复的元素,这是一个有状态的操作

sorted 确保流中的元素在后续的操作中,按照比较器(Comparator)决定的顺序访问,这是一个有状态的操作

limit 保证后续的操作所能看到的最大数量的元素,这是一个有状态的短路的操作

substream 确保后续的操作只能看到一个范围的(根据index)元素

末端的操作

forEach 对流中的每个元素执行一些操作

toArray 将流中的元素倾倒入一个数组

reduce 通过一个二进制操作将流中的元素合并到一起

collect 将流中的元素倾倒入某些容器,例如一个Collection或Map

min 根据一个比较器找到流中元素的最小值

max 根据一个比较器找到流中元素的最大值

count 计算流中元素的数量

末端的操作

anyMatch 判断流中是否至少有一个元素匹配断言。这是一个短路的操作

allMatch 判断流中是否每一个元素都匹配断言。这是一个短路的操作

noneMatch 判断流中是否没有一个元素匹配断言。这是一个短路的操作

findFirst 查找流中的第一个元素。这是一个短路的操作

findAny 查找流中的任意元素,可能对某些流要比findFirst代价低。这是一个短路的操作

Nashorn JavaScript

New Time API

Collections API

Concurrency API

IO/NIO API

THE END

Halo9Pan