java8新特性之lambda表达式

star2017 1年前 ⋅ 2115 阅读

java8简化代码

在java8之前我们是没办法传递函数,只能传递值,而且在java8之前,我们要对某个列表进行排序,你会使用内部类,或者在使用Swing的时候,会大量使用内部类,如:

List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });

这样代码不但臃肿,而且不利于阅读,在java8中,出现了一种新语法,叫lambda表达式。先来看看使用lambda表达式之后,会变成什么样子?

 List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        Collections.sort(names, (String a, String b) -> {
            return b.compareTo(a);
        });

还可以变的更短,如

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

还可以写成,如

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

还可以优化为:

names.sort((a, b) -> b.compareTo(a));
或者
names.sort(Comparator.reverseOrder());

这样的代码是不是变的简洁很多,本来7行代码直接变成了1行代码。这样大大的简化的代码。

函数式接口

什么是函数式接口?

1、如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
2、如果我们在某个接口上声明了FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。
3、如果某个接口只有一个抽象方法,但我们并没有给该接口声明FunctionalInterface注解,那么编译器依旧会将该接口看作是函数式接口。
4、如果在该接口上定义了java.lang.Object的public方法,那么不会记为抽象方法。
5、其中默认方式不是抽象的,所有可以有多个默认方法在定义了函数式接口中。
6、函数式接口可以用lambda表达式,方法引用,构造函数引用来创建。
7、必须是接口,如果是枚举,类,那么编译器会报错。

函数式接口的示例如:

@FunctionalInterface
public interface FunctionalInterface01 {
    //抽象方法
    void test();
    //默认方法
    default void test01(){
    }
    //默认方法
    default void test02(){
    }
    //是java.lang.Object的方法
    @Override
    String toString();
}

如果定义多个抽象方法,那么就会报错 :
image.png
如果是Class,也会报错:
image.png

为什么有函数式接口?

为了更好的使用lambda表达式,它是lambda的基础。

如何使用?

直接看例子

public class FunctionalClass01 {
    public void myTest(FunctionalInterface01 functionalInterface01){
        System.out.println(1);
        functionalInterface01.test();
        System.out.println(2);
    }
    public static void main(String[] args) {
        FunctionalClass01 class01 = new FunctionalClass01();
        class01.myTest(new FunctionalInterface01() {
            @Override
            public void test() {
                System.out.println("test");
            }
        });
    }
}

输出结果为:

1
test
2

也可以使用lambda表达式:

 public static void main(String[] args) {
        FunctionalClass02 class01 = new FunctionalClass02();
        class01.myTest(() -> System.out.println("test"));
    }

输出的结果跟上面的示例是一样的。
这就是为什么函数式接口只能有一个方法,因为() -> System.out.println("test")表示就是执行FunctionalInterface01接口里的test方法,看示例:

FunctionalInterface01 functionalInterface01 = ()->System.out.println("test");

java8中内置了很多函数式接口,像Comparator或者Runnable等,所有可以直接用lambda表达式来使用他们。

lambda表达式

lambda是通过上下文来知道是什么类型,如果光写一个()->{};编译器不知道这个lambda是什么类型。在java中,lambda表达式是对象,不是函数。在java8之后使用了大量的lambda表达式,尤其是stream。
lambda表达式的基本结构:
->左边是参数,->右边是执行体。如(参数)->{body},比如:
1、(param1,param2,param3...)->{body}
2、(type1 arg1,type2 arg2...)->{body}
3、arg1->方法
4、arg1->{body}
5、方法引用
6、构造方法引用

一个lambda有零个或多个参数,参数的类型可以明确声明,也可以根据上下文推断,所有参数都需要在圆括号里头,多个参数之间用短号隔开,空圆括号表示参数为空。当只有一个参数时且类型可以推断出来,圆括号可以省略,lambda表达式的主体可以是一条或多条语句,如果lambda表达式主体只有一条语句,那么{}可以省略,主体代码的返回值需要和返回类型一致。如果lambda主体有多条语句,那么必须要用{},形成代码块。

方法引用

是特殊的lambda表达式。
1、类名::静态方法名

public static String myString(String str){
        System.out.println(str);
        return str;
    }
    public static void main(String[] args) {
        List<String> nameList = Arrays.asList("zhangsan","lisi");
        nameList.forEach(ConstructorReferences::myString);
    }

2、引用名(对象名)::实例方法名

public String myString(String str){
        System.out.println(str);
        return str;
    }
    public static void main(String[] args) {
        ConstructorReferences1 constructorReferences1 = new ConstructorReferences1();
        List<String> nameList = Arrays.asList("zhangsan","lisi");
        nameList.forEach(constructorReferences1::myString);
    }

3、类名::实例方法名

public class ConstructorReferences2 {
    private String name;
    private int age;
    public ConstructorReferences2(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public int compareByAge(ConstructorReferences2 constructorReferences2){
        return this.age-constructorReferences2.age;
    }
    public static void main(String[] args) {
        ConstructorReferences2 constructorReferences1 = new ConstructorReferences2("zhangsan",20);
        ConstructorReferences2 constructorReferences2 = new ConstructorReferences2("lisi",22);
        List<ConstructorReferences2> nameList = Arrays.asList(constructorReferences1,constructorReferences2);
        nameList.sort(ConstructorReferences2::compareByAge);
    }
    get/set方法
}

构造方法引用

语法:类名::new
如:

public String getString(Supplier<String> supplier){
        return supplier.get()+"test";
    }
    public static void main(String[] args) {
        MethodReferences4 methodReferences4 = new MethodReferences4();
        System.out.println(methodReferences4.getString(String::new));
    }

局部变量

@FunctionalInterface
public interface Converter<F, T> {
    T convert(F from);
}
final int num = 1;
        Converter<Integer, String> stringConverter =
                (from) -> String.valueOf(from + num);
        System.out.println(stringConverter.convert(2));    // 3

下面的写法编译失败:
image.png
image.png
变量必须设置为final,才可以。

静态变量

与局部变量相反,我们可以从lambda表达式中读写实例字段和静态变量。如:

public class StaticVariables {
    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);
        };
    }
}

源码地址
查看lambdaexpressionsfunctionalinterface包下的类。

本文为博主原创文章,未经博主允许不得转载。
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: