今天想啃一啃jdk8的新特性,感觉好多新的东西出来因为自己的懒惰都没有学习,其实很多东西学会了可以大大提高效率。现在整理出来自己的一些心得(未必准确仅供参考)
一、Lambda表达式:
jdk8中推出了新特性Lambda表达式,随着Lambda表达式的产生,也引入了一个新的概念函数式编程,即可以进行函数传递,我的理解就是可以把函数当成参数传来传去的思想就是比较浅的理解函数式编程。
jdk8中提出了函数式接口并用@FunctionalInterface来对想实现自己函数式接口的开发人员进行限制。
1.1函数式接口
函数式接口就是只有一个抽象方法的接口,可以有default方法(同为jdk8的新特性)但是只能有一个抽象方法,这样的接口叫做函数式接口。
1.2@FunctionalInterface
这个注解用在类上表示要声明一个函数式接口,作用就是在编译器检查含有该注解的接口时,如果接口中有2个或2个以上的抽象方法就会在编译期间报错(与@Override注解功能很像)。
1.3Lambda表达式
Lambda表达式个人理解是代替内部类的冗余写法而诞生(但在一些细节上和内部类有明显差异,稍后会提到)。
比如原来的线程启动(伪代码 写错勿见怪理解大意就好):
new Thread(new Runnable(){
public void run(){
System.out.println("dadada");
}
}).start();
在有lambda表达式之后可以简写成:
new Thread(()->System.out.println("dadada"));
上面说到函数式编程,所以你可以将lambda表达式赋值给一个函数式接口:
Runnable run1 = ()->System.out.println("dadada");
对于有参数的例如Comparator接口:
Comparator<String> com = (s1,s2)->s1.compareTo(s2);
通常情况下lambda表达式是可以省略参数类型的,这里说通常情况下,不可省略的列子很多 百度即可
lambda表达式隐含了return如果方法体中只有一个表达式可以省略return并省略{}
二、方法引用:
方法引用就是Lambda表达式的简化版本(也就是说方法引用返回的也是一个Lambda表达式更进一步说也是一个函数式接口)
1.1几种常用的表现形式
1)引用静态方法
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
2)特定实例对象的方法引用
和第一种类似只不过::前从类变成了实例化的对象变量
Person p = new Person();
p::say 等价于lambda表达式 ()->p.say()
3)构造方法引用
Person::new 等价于lambda表达式 ()->new Person()
剩下的一些方法引用不好理解并且用的不多只简单介绍几种常用的
另外,在学习过程中我发现设计方法引用的人应该是这么想的:
哎lambda表达式中如果只需要调用一个之前就写好的方法还是不够简单还要写参数的括号,箭头,最后其实只是调用了别人写好的一个方法,太麻烦了,不如干脆点,这个方法如果已经写好并且我有访问权限,莫不如直接代替lambda表达式,什么?你说那怎么确定参数列表,你怎么知道你的方法引用一定就对毕竟你连参数几个都没写?? 好,那我问你lambda怎么确定?(肯定是根据要实现的接口啦,实现的接口规定两个参数那lambda中就是两个参数) 既然lambda能确定,我在用方法引用的时候是不是也能确定,如果真的有已经写好的并且参数和返回值和lambda中的参数都能对应上那我为什么还要再浪费笔墨???结案
三、理解上的难点:
方法引用是Lambda表达式简写所以方法引用就是Lambda表达式,Lambda表达式返回一个函数式接口,即返回一个实现了函数式接口的类似与匿名内部类的函数。Lambda的行为和要实现的接口有很大关系。上代码:
先声明一个函数式接口(用它作为创建任意类型对象的生产者)
@FunctionalInterface
public interface Generator<T> {
T next();
}
声明一个实体类Person:
public class Person {
private long id;
private String name;
private int age;
private String job;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
在main函数中实现一个Generator(用lambda代替匿名内部类实现)
public class StreamTest1 {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
//Generator<Person> personGenerator = ()->new Person();
Generator<Person> pGenerator = Person :: new;
Person p1 = pGenerator.next();
p1.setName("Smith");
p1.setAge(18);
p1.setJob("java");
Person p2 = pGenerator.next();
p2.setName("Tom");
p2.setAge(28);
p2.setJob("Leader");
Person p3 = pGenerator.next();
p3.setName("John");
p3.setAge(30);
p3.setJob("Boss");
list.add(p1);
list.add(p2);
list.add(p3);
System.out.println(list);
}
}
注意看这里注释的代码就是lambda的表现形式,放开的代码是lambda基础上的简化也就是方法引用形式。
再次强调理解的关键在于:lambda表达式可以理解为一个接口的实现类,只不过是以函数形式体现了出来。
在这段代码中的理解就是之所以lambda表达式没有参数是因为实现的Generator接口中只含有一个没有参数的抽象方法。