Java8新特性之Lambda

接口的默认方法

  1. Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法

    interface Formula {
        double calculate(int a);
        default double sqrt(int a) {
        	return Math.sqrt(a);
        }
    } 
    
  2. 例子: Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用

    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 表达式:

  1. 第一种:

    Collections.sort(names, (String a, String b) -> {
    	return b.compareTo(a);
    });
    
  2. 第二种: 对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字

    Collections.sort(names, (String a, String b) -> b.compareTo(a)
    
  3. 第三种: Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型

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

函数式接口

  1. 每一个lambda表达式都对应一个类型,通常是接口类型。而***函数式接口*是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

  2. 可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。

    @FunctionalInterface
    interface Converter<F, T> {
    	T convert(F from);
    }
    Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
    Integer converted = converter.convert("123");
    System.out.println(converted); // 123
    

方法与构造函数引用:

  1. Java 8 允许你使用::关键字来传递方法或者构造函数引用

    Converter<String, Integer> converter = Integer::valueOf;
    Integer converted = converter.convert("123");
    System.out.println(converted); // 123
    
  2. 引用一个对象的方法:

    converter = something::startsWith;
    String converted = converter.convert("Java");
    System.out.println(converted); // "J"
    
  3. 接下来看看构造函数是如何使用::关键字来引用的,首先我们定义一个包含多个构造函数的简单类

    class Person {
        String firstName;
        String lastName;
        Person() {}
        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
    
    //指定一个用来创建Person对象的对象工厂接口
    interface PersonFactory<P extends Person> {
    	P create(String firstName, String lastName);
    }
    
    //使用例子
    //只需要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数
    PersonFactory<Person> personFactory = Person::new;
    Person person = personFactory.create("Peter", "Parker");
    

Lambda 作用域:

  1. 在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量

  2. 访问局部变量:

    final int num = 1;
    Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);	
    stringConverter.convert(2); // 3
    
    
    //但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:
    nt num = 1;
    Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);
    stringConverter.convert(2); // 3
    
    
    //不过这里的num必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:在lambda表达式中试图修改num同样是不允许的。
    int num = 1;
    Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);
    num = 3;
    
  3. 访问对象字段与静态变量: 和本地变量不同的是,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);
            };
        }
    }
    
  4. 访问接口的默认方法: 还记得之前的formula例子么,接口Formula定义了一个默认方法sqrt可以直接被formula的实例包括匿名对象访问到,但是在lambda表达式中这个是不行的。Lambda表达式中是无法访问到默认方法的,以下代码将无法编译:

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

新接口介绍:

  1. Predicate接口: 只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)

    Predicate<String> predicate = (s) -> s.length() > 0;
    predicate.test("foo");
    // true
    predicate.negate().test("foo"); // false
    Predicate<Boolean> nonNull = Objects::nonNull;
    Predicate<Boolean> isNull = Objects::isNull;
    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();
    
  2. Function 接口: 有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

    Function<String, Integer> toInteger = Integer::valueOf;
    Function<String, String> backToString = toInteger.andThen(String::valueOf);
    backToString.apply("123"); // "123"
    
  3. Supplier 接口: 返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

    Supplier<Person> personSupplier = Person::new;
    personSupplier.get(); // new Person
    
  4. Comparator 接口: Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方

    Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
    Person p1 = new Person("John", "Doe");
    Person p2 = new Person("Alice", "Wonderland");
    comparator.compare(p1, p2); // > 0
    comparator.reversed().compare(p1, p2); // < 0
    
  5. Optional 接口:

    1. Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一节中将要用到的重要概念,现在先简单的看看这个接口能干什么:
    2. Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返
      回null而是返回Optional。
    Optional<String> optional = Optional.of("bam");
    optional.isPresent(); // true
    optional.get(); // "bam"
    optional.orElse("fallback"); // "bam"
    optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
    
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隐 风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值