java8新特性理解

一些关于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

posted @ 2023-05-04 21:27  tryingWorm  阅读(30)  评论(0编辑  收藏  举报