一些关于java8新特性理解
引例
在java中,考虑实现按学号顺序对学生List进行排序的功能。
一. java8以前的实现方式:匿名内部类
如果不使用java8的新特性,我们实现该功能通常会使用匿名内部类的方法。
import java.util.*;;
class Student{
private int id;
private String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}
public int getId(){
return this.id;
}
public String getName(){
return this.name;
}
}
public class Example{
public void sortStudent(List<Student> students){
students.sort(new Comparator<Student>() {
@Override
public int compare(Student stu1, Student stu2){
return stu1.getId() - stu2.getId();
}
});
}
}
因为每一次比较时,比较方式是不同的,因此在每一次比较时,我们都是创建了一个Comparator的子类,这个类没有名字,且只有一个实例,这就是匿名内部类,它等效于显式地创建一个Comparator的子类,再传递给sort这个子类的实例。对代码
public class Example{
public void sortStudent(List<Student> students){
students.sort(new Comparator<Student>() {
@Override
public int compare(Student stu1, Student stu2){
return stu1.getId() - stu2.getId();
}
});
}
}
我们可以等效地写成
class StudentComparator implements Comparator<Student>{
@Override
public int compare(Student stu1, Student stu2){
return stu1.getId() - stu2.getId();
}
}
public class Example{
public void sortStudent(List<Student> students){
students.sort(new StudentComparator());
}
}
可以看到,无论是使用匿名内部类还是显式声明的子类,代码都很长,破坏了代码的可读性。java8为了改善这种情况,引入了一系列新特性。
二.函数式编程
在java编程时,有时会希望传入一个方法作为参数来实现某样功能。比如: List.sort(比较方法)
中,我们希望传入的比较方法
其实就是一个函数。这种将函数作为参数传入的思想被称为函数式编程。
在java中,我们认为“一切皆对象”,所以具体来看,java中List的sort方法声明是sort(Comparator<? super E> c)
参数是一个Comparator类的对象,查看Comparator类的源代码,我们发现它只有一个抽象方法(重写的Object类方法equals除外),和平常的接口很不一样,因此我们引入函数式接口
的概念。
三.函数式接口
这是java8引进的一个新概念。指的是只有一个抽象方法的接口(除了重写Object类方法以外),通常,这样的方法会被@FunctionalInterface
所修饰(不被这个注解修饰,但满足只有一个抽象方法的接口(除了重写Object类方法以外)
这一条件的接口也是函数式接口)。下面是一个例子。
@FunctionalInterface
interface FunctionalInterfaceA{
public int method();
}
四.lamda表达式
lamda表达式是为了简化实现函数式接口匿名内部类的书写而引进的。对于下面的代码
public void test(){
FunctionalInterfaceA a = new FunctionalInterfaceA() {
public int method(){
return 1;
}
};
}
使用lamda表达式就可以写成
public void test(){
FunctionalInterfaceA a = () -> 1;
}
注意:lamda表达式在实际上就是创建了一个匿名内部类,但是lamda表达式仅在实现函数式接口的匿名内部类中能使用,若内部类要重写多个方法,是不能用lamda表达式实现的。具体lamda表达式的语法,详见https://www.runoob.com/java/java8-lambda-expressions.html。
五.方法引用
观察上面的lamda表达式使用的格式FunctionalInterfaceA a = () -> 1;
,我们发现形式上是很类似于变量 = 函数
的,这是因为函数式接口FunctionalInterfaceA
可以由它的抽象方法 public int method()
唯一确定。既然有变量 = 函数
的形式,我们自然想到,当两函数的参数列表和返回值相同时,我们可以用一个函数为另一个函数赋值。这就是方法引用的思想。
观察下面的代码
class A{
private int attr;
public A(int a){
attr = a;
}
public int methodA(){
return attr;
}
}
class Example{
public static void main(String[] args) {
A a = new A(10000);
FunctionalInterfaceA b = a::methodA;
}
}
@FunctionalInterface
interface FunctionalInterfaceA{
public int method();
}
因为A的methodA方法的函数签名与返回值和FunctionalInterfaceA的抽象方法相同,故可以用methodA来代替method,其中的FunctionalInterfaceA b = a::methodA;
用lamda表达式可以写为FunctionalInterfaceA b = () -> a.methodA();
,更多方法引用的语法,见https://blog.csdn.net/ShuSheng0007/article/details/107562812
六. Stream流
Stream流是java8为了统一集合,数组对其中元素进行变换的操作所引入的新类型,类似python中序列的概念。
Stream中的方法主要分为三类:获取操作,处理操作,终结操作
获取操作主要是为了获取流,比如List.stream()
,其返回值是Stream
处理操作主要是对流中的数据进行处理,比如Stream.mapToInt(ToIntFunction<? super T> mapper)
,其返回值也是Stream,这就可以让Stream能类似管道一样,将多个中间的处理操作进行组合,从而实现较为复杂的操作。
终结操作是将流转为新的类型进行输出,比如Stream.collect(Collector<? super T, A, R> collector)
,将数据通过不同的收集器收进不同的集合中。
下面举一个将Set<Integer>
转为int[]
的例子
public int[] set2Array(Set<Integer> set){
return set.stream().mapToInt(x -> x).toArray();
}
更多关于Stream的信息,详见https://www.runoob.com/java/java8-streams.html
标签:lamda,函数,int,特性,public,理解,FunctionalInterfaceA,java8 From: https://www.cnblogs.com/tryingWorm/p/17372566.html