java 1.8 函数式编程

接口的默认方法

java 1.8之前,接口里面只能定义抽象方法,1.8之后再接口里面可以定义非抽象的方法了,主要使用default关键字定义,如下:

interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

在接口的实现类里面可以直接调用接口的默认方法

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

Lambda 表达式

java 1.8之前要实现集合里面的元素的排序一般都是写一个Comparator接口的匿名实现类,如下:

List<String> names = Arrays.asList("张三", "李四", "隔壁老王");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

上面这种写法太麻烦了,java 1.8中增加了一个更加简单的语法:lambda表达式来代替这种写法。

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

更加简单的可以这样写:

Collections.sort(names, (String a, String b) -> b.compareTo(a));

甚至这样写:

names.sort((a, b) -> b.compareTo(a));

为什么可以这么写,往下看慢慢来分析。

功能性接口(@FunctionalInterface)

就像我们以前所认知的一样,java是一种强类型匹配的语言,那么这种lambda表达式怎么能够和java的类型对应起来呢?所以java 1.8提出了@FunctionalInterface功能性接口的概念。功能性接口首先是一个接口,然后只能有一个自带的抽象方法(继承的除外)。每一个lambda表达式将会这个抽象方法相匹配。通过@FunctionalInterface注解告知编译器这是一个功能性接口(也可以不写,编译器自己会知道)

例如:

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);//后面的这个lambda表达式,所对应的方法就是Converter接口里面的convert方法,其中“(from)”标示参数,“Integer.valueOf(from)”标示方法的返回值, “->”是语法规则
Integer converted = converter.convert("123");//在方法的调用的时候,参数的类型是通过泛型进行匹配的。
System.out.println(converted); // 123

方法引用

上面的方法其实还可以通过静态方法引用再简化,java 1.8允许通过“::”关键字传递方法引用。

静态方法引用

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");//对convert方法的调用直接调用Integer.valueOf(String)方法,并返回
System.out.println(converted);   // 123

实例方法引用

class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");//对convert方法的调用直接调用something实例的,并返回startsWith(String s)方法.
System.out.println(converted);    // "J"

构造方法引用

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");//调用 create方法,自动寻找对应的Person实例的构造方法,并返回

Lambda表达式的变量作用域

在lambda表达式里面访问外部变量和匿名对象十分的访问方式十分相似。

访问局部变量

final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);//访问局部变量,局部变量要定义为final类型

stringConverter.convert(2);     // 3

如果局部变量不定义为final类型,只要在lambda里面没有改变这个变量的值,可以运行,如果改变了,编译过不了。

访问实例变量和静态变量

class Lambda4 {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Converter<Integer, String> stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };

        Converter<Integer, String> stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}

访问接口里面的默认方法

在lambda里面访问默认方法直接编译错误。

Formula formula = (a) -> sqrt( a * 100);//complie error

分析一下为什么可以这么写:方式用到接口泛型的地方,所有的参数类型,编译器会自动推断。lambda表达式里面的参数也是自动推断出来的。

猜你喜欢

转载自blog.csdn.net/c601097836/article/details/51189661