Lambda表达式
Lambda是1.8中新加的语法糖(不是内部类,匿名类的语法糖),Lambda表达式逻辑上可以理解成实现一个匿名类,这个类只定义了一个方法。
Lambda有两个好处:
1.编码简洁;
2.配合1.8中的新编程方式:函数式接口(FunctionalInterface);
Lambda表达式的格式:(parameters) -> expression,Comparable实现代码示例:
public class Book {
private int pages;
public Book(int pages){
this.pages = pages;
}
private Comparable<Book> comparable = (x) ->{
return Integer.compare(this.pages,x.pages);
};
public static boolean isLarge(Book a){
if(a.getPages() > 1000)
return true;
else
return false;
}
public int compareTo(Book a){
return comparable.compareTo(a);
}
public int compareToSum(Book a,Book b){
return comparable.compareTo(a);
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
}
函数式接口
在上文中我们提到了这个函数式接口的概念,在JDK1.8中对这种只定义唯一一个抽象方法的接口叫做函数式接口,可以用@FunctionalInterface注解来描述,不过这个注解是非必须的。下面是常用的四个核心接口:
Lamdba表达式就是对这种函数式接口的便捷编程方式。
函数引用(方法引用)
它和Lambda一样都是返回一个函数式接口的实例化对象,可以理解成对一个非函数式接口的某一个函数(方法)实现函数式接口的拆分实例化。换句话说是对函数式接口的另一种支持,是Lambda表达式的一种精简。具体语法如下:
普通函数引用:
1.实例::方法
Book a = new Book(10000);
//以下两种定义是等价的
Function<Book,Integer> f1 = a::compareTo;
Function<Book,Integer> f2 = (x)-> a.compareTo(x);
2.类::静态方法
//以下两种定义是等价的
Predicate<Book> p1 = Book::isLarge;
Predicate<Book> p2 = x -> Book.isLarge(x);
3.类::方法
//以下两种定义是等价的
Function<Book, Integer> pg1 = Book::getPages;
Function<Book, Integer> pg2 = x -> x.getPages();
构造函数引用
1.普通对象,类::new
//以下两种定义是等价的
Function<Integer,Book> c1 = Book::new;
Function<Integer,Book> c2 = x -> new Book(x);
2.数组,Type[]::new
//以下两种定义是等价的
Function<Integer,Book[]> bc1= Book[]::new;
Function<Integer,Book[]> bc2= x -> new Book[x];
Stream API
Stream就是流,前面是函数式编程的引入,这里的Stream就是对函数式编程的一个应用了,如果我们看Stream这个接口就会发现,里面的入参基本都是函数式接口。看过这个接口定义的方法之后就知道,这个接口的作用是简化Collection(集合)的操作。
常用的方法:
1.构造Stream:
Collection和Arrays都增加了生成Stream的方法.stream();
Stream提供了自己的静态方法.of(),.iterate()和.generate(),其中后两个生成的是无限流。
2.对Stream进行操作,生成另一个Stream:
操作包括filter,limit,skip,distinct,map,sorted。其中map并不是生成map而是根据传入的规则对原始Stream中每个元素进行操作,得到新的Stream。
3.不生成Stream:
主要是查找和匹配。这里说两个特殊操作:reduce返回一个Optional(这个也是1.8新加入的成员,下面会有具体描述)对象,逻辑是对Stream中的元素两两操作,最终返回一个值。这个参数有一定规则。附上代码大家感受一下。代码示例如下:
String[] strings = new String[]{"123","224","167"};
Stream<String> s = Arrays.stream(strings);
Optional<String> ss = s.map(x -> x.substring(0,1)).reduce(String::concat);
//或者
//Optional<String> ss = s.map(x -> x.substring(0,1)).reduce((x,y) -> x.concat(y));
注意,注释中的入参只能是两个。第二个操作是collect,返回一个Collection对象,等于对Stream数据的归集生成集合List<String> ss = s.map(x -> x.substring(0,1)).collect(Collectors.toList());
注意:Stream只能操作一次,不能重复操作,需要对一个Stream进行多次操作的话需要生成多个Stream对象。
并行和串行
Stream api中声明可以通过parallel()与sequential()方法在并行流和串行流之间进行切换。 这两个方法和上面提到的方法不同,不会让当前操作的Stream失效。其中并行流的实现使用了fork/join框架。fork拆分,join汇总,采用 “工作窃取”模式(work-stealing这个模式以后具体讲解),下面是fork/join框架的两个核心接口:RecursiveAction(无返回值) 和RecursiveTask(有返回值)。
Optional容器
就个人而言Optional的引入作用并不是很大(纯属个人感觉),首先并没有避免NPE,其次也没有使代码简化(还是要判断),最后还会浪费一些性能。主要作用就是强迫开发人员检验。
接口中可以定义默认方法和静态方法了
在接口中可以使用default和static关键字来修饰接口中定义的普通方法,例子可以看上文中提到的函数式接口中定义的方法。这是一种新的规范,default主要因为原有接口修改,为了上下兼容增加的默认实现,static使得可以直接用接口引用静态方法了。
LocalDate | LocalTime | LocalDateTime
这三个日期都是不可变的,适用于多线程环境。其他和普通的类相似,在此不再做过多的赘述。
总结
JDK1.8主要是主要升级点在于函数式接口的引入(Lambda表达式,方法引用),以及函数式编程的实践(Stream API)。