专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

简单回顾Java 8 (一)

Java 8 与 Scala

在这里必须要提到一位大牛:Martin Odersky。他是一位编译器以及编程的狂热爱好者,JDK5.0和JDK8.0的编译器便是Martin Odersky完成的。他创造了两种编程语言(Pizza & Scala),并将它们的一部分特性带入到了Java当中,推动了Java的进化与发展。

三个单词表达自己最中意的Java8特性,那就是:Lambda,Stream,Optional。

Lambda表达式

Lambda表达式简化了匿名类的方法重写的繁杂声明,并且可以根据接口的泛型自动推断Lambda表达式中的参数类型。它的引入为Java的函数式编程打下了基础。

在Java8之前,如果要用匿名类直接实现一个接口的方法,则需要这样:

Consumer<String> stringConsumer = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println("Hello,"+s);
    }
};

在引入了Lambda表达式之后,一行即可搞定:

Consumer<String> consumer = str -> System.out.println("Hello,"+str);

函数式接口

Java中的函数式编程是基于接口去实现的。Lambda语法实际上就是简化了对抽象方法的实现。可这又带来了一个问题:如果一个接口声明了多个抽象方法,那么Lambda语法应该实现哪一个呢?

如果Lambda表达式可以通过参数列表自动判断我希望重写哪个抽象方法,那一定很酷,可是目前它并不支持这么做。也就是说Lambda表达式仅可用于只声明了一个抽象函数的接口

Java8使用注解@FunctionalInterface来对想要用于函数式编程的接口做约束:该接口的抽象方法有且只有一个,否则报出编译错误。

Java8 提供了4种核心类型的函数式接口:

函数式接口 参数类型 返回类型 内部方法 特征
Consumer<T> T void void accept(T t) 只消费,不返回
Supplier<T> void T T get() 只返回,不消费
Function<T,R> T R R apply(T t) 提供T,返回R
Predicate<T> T Boolean Boolean test(T t) 提供T,作检验

方法引用

有些情况下,某个Lambda表达式纯粹是简单地调用了其它类的方法,而没有其它多余的动作
比如上一段的Lambda表达式如果变为了这个样子:

Consumer<String> consumer = str -> System.out.println(str);

对于接收到的str变量,该函数除了原封不动地再将它抛给了System.out.println方法,便再也没有额外操作了。因此,干脆连()->{}格式也省略掉,直接声明该函数引用System.outprintln方法:

Consumer<String> consumer1 = System.out::println;

这种“偷懒”的引用是有条件的:被引用的方法和你要实现的抽象方法是“同调”的。即它们的参数类型,返回的数据类型完全一致,只不过具体的执行流程被“调包”为了被引用的方法。

104_1.png

如果引用的是个很繁琐的”套娃函数”也没有关系。只要保证最外层函数的参数列表最终返回的数据类型与抽象函数的参数类型,返回类型保持一致也无伤大雅。

104_2.pngJava中没有哪个方法是脱离于Object而独立存在,因此要声明被引用的方法源自哪里。格式为 Source::method。这里的 来源可以是类,或者实例,或者是构造器。方法可以是 静态方法,也可以是实例方法

方法引用可以分为以下四类:

类型 语法 Lambda表达式写法 注释
静态方法引用 Clazz::Method arg -> Clazz.Method(arg) Method是静态方法
实例方法引用 inst::method arg -> inst.method(arg) method是实例方法
对象方法引用 Clazz::method (inst,arg) -> inst.method(arg) inst是Clazz的一个实例
构造器引用 Clazz::new (args) -> new Clazz(args) 调用的构造器取决于上下文

其中,对象方法引用可能稍稍费解一些。当在以下情境时,可以使用对象方法引用来简化:

Lambda参数列表中的前一个参数用于调用它的一个实例方法,而后一个参数是该实例方法的参数。

假设有一个这样的场景:一个工厂类Factory,有一个公开的方法produce(Properties prop)。它需要一个配置类Properties做参数,返回一个产品Product类实例。现在希望用Lambda表达式来描述这个方法:

Function2<Factory,Properties,Product> produce = (factory,prop)->factory.produce(prop);

实际上这个三元函数引用的是Factory类的produce实例方法,并希望向该方法传入Properties对象,并返回一个Product对象。因此这个Lambda表达式还可以改成:

Function2<Factory,Properties,Product> produce = Factory::produce;

对象方法引用可用在简化Comparator<E>接口的声明。比较器的功能实现实际上是调用了一个对象(该对象的类实现了Comparable<E>接口)的compareTo方法,该方法接收外部的另一个同类对象,并通过返回正负值来实现大小比较。

Comparator<Product> comparator = Product::compareTo;
products.sort(Product::compareTo);

至于构造器引用,默认使用无参构造函数。如果这个函数式接口可提供参数列表,则编译器会使用参数列表相匹配的构造器

  //调用的是Product()
  Supplier<Product> productSupplier = Product::new
  //调用的是Product(Integer product_id)
  Function<Integer,Product> productFunction = Product::new;

文章永久链接:https://tech.souyunku.com/42004

未经允许不得转载:搜云库技术团队 » 简单回顾Java 8 (一)

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们