澳门新葡萄京官网注册 1

澳门新葡萄京官网注册浅谈Java 8的函数式编程

关于“Java
8为Java带来了函数式编程”已经有了很多讨论,但这句话的真正意义是什么?

陈光剑

Java8简单了解Lambda表达式与函数式接口,java8lambda

Java8被称作Java史上变化最大的一个版本。其中包含很多重要的新特性,最核心的就是增加了Lambda表达式和StreamAPI。这两者也可以结合在一起使用。首先来看下什么是Lambda表达式。

使用Lambda表达式不仅让代码变的简单、而且可读、最重要的是代码量也随之减少很多。然而,在某种程度上,这些功能在Scala等这些JVM语言里已经被广泛使用。

并不奇怪,Scala社区是难以置信的,因为许多Java
8里的内容看起来就像是从Scala里搬过来的。在某种程度上,Java
8的语法要比Scala的更详细但不是很清晰,但这并不能说明什么,如果可以,它可能会像Scala那样构建Lambda表达式。

澳门新葡萄京官网注册,一方面,如果Java继续围绕Lambda来发展和实现Scala都已经实现的功能,那么可能就不需要Scala了。另一方面,如果它只提供一些核心的功能,例如帮助匿名内部类,那么Scala和其他语言将会继续茁壮成长,并且有可能会凌驾于Java之上。其实这才是最好的结果,有竞争才有进步,其它语言继续发展和成长,并且无需担心是否会过时。

Lambda表达式,维基百科上的解释是一种用于表示匿名函数和闭包的运算符,感觉看到这个解释还是觉得很抽象,接下来我们看一个例子

public class SwingTest {
  public static void main(String[] args) {
    JFrame jFrame = new JFrame("My JFrame");
    JButton jButton = new JButton("My JButton");

    jButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {        
        System.out.println("Button Pressed!");
      } 
    }); 

    jFrame.add(jButton); jFrame.pack(); 
    jFrame.setVisible(true); 
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
  }
}

这是一段Swing编程中的代码,给Button绑定一个监听事件,当点击Button时会在控制台输出”ButtonPressed!”内容。这里使用了创建了一个匿名内部类的实例来绑定到监听器,这也是以往比较常规的代码组织形式。但是仔细看一下我们会发现,实际上我们真正关注的就是一个ActionEvent类型的参数e和向控制台输出的语句System.out.println(“ButtonPressed!”);。

如果将上段程序中以匿名内部类的方式创建接口实例的代码替换成Lambda表达式后,代码如下

public class SwingTest {

public static void main(String[] args) {
  JFrame jFrame = new JFrame("My JFrame");
  JButton jButton = new JButton("My JButton");

  jButton.addActionListener(e -> System.out.println("Button Pressed!"));

  jFrame.add(jButton);
  jFrame.pack();
  jFrame.setVisible(true);
  jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

关注最中间部分代码的变化,由原来的6行代码,现在1行就可以实现了。这就是Lambda表达式的一种简单形式。
可以看出Lambda表达式的语法是

(param1,param2,param3) -> {

//todo
}

这里参数的类型程序可以根据上下文进行推断,但是并不是所有的类型都可以推断出来,此时就需要我们显示的声明参数类型,当只有一个参数时小括号可以省略。当todo部分只有一行代码时,外边的大括号可以省略。如我们上面的示例

那么除了代码简洁了,Lambda表达式还给我们带来了什么变化吗?

我们回忆一下,在Java中,我们是否无法将函数作为参数传递给一个方法,也无法声明返回值是一个函数的方法。在Java8之前,答案是肯定的。

那么,在上面的例子中我们居然可以将一段代码逻辑作为参数传递给了监听器,告诉监听器事件触发时你可以这么做,而不再需要以匿名内部类的方式作为参数。这也是Java8带来的另一新特性:函数式编程。

支持函数式编程的语言有很多,在JavaScript中,把函数作为参数传递,或者返回值是一个函数的情况非常常见,JavaScript是一门非常常见的函数式语言。

Lambda为Java添加了缺失的函数式编程的特性,使我们能将函数当做一等公民看待。

在函数式编程语言中,Lambda表达式的类型是函数。而在Java中,Lambda表达式是对象,它们必须依附于一类特别的对象类型——函数式接口(FunctionalInterface)。

接下来我们看下函数式接口的定义:

如果一个接口中,有且只有一个抽象的方法(Object类中的方法不包括在内),那这个接口就可以被看做是函数式接口。

@FunctionalInterface
public interface Runnable {
  /**
   * When an object implementing interface <code>Runnable</code> is used
   * to create a thread, starting the thread causes the object's
   * <code>run</code> method to be called in that separately executing
   * thread.
   * <p>
   * The general contract of the method <code>run</code> is that it may
   * take any action whatsoever.
   *
   * @see   java.lang.Thread#run()
   */
  public abstract void run();
}

来看下Runnable接口的声明,在Java8后,Runnable接口多了一个FunctionalInterface注解,表示该接口是一个函数式接口。但是如果我们不添加FunctionalInterface注解的话,如果接口中有且只有一个抽象方法时,编译器也会把该接口当做函数式接口看待。

@FunctionalInterface
public interface MyInterface {
  void test();
  String toString();
}

MyInterface这也是一个函数式接口,因为toString()是Object类中的方法,只是在这里进行了复写,不会增加接口中抽象方法的数量。

(到这里额外提一下,Java8中,接口里面的方法不仅仅只能有抽象方法,也可以有具体实现了的方法,被称作默认方法(defaultmethod),这部分后面会具体介绍)

既然在Java中,Lambda表达式是对象。那么这个对象的类型是什么呢?我们再回顾下SwingTest程序,这里以匿名内部类的方式创建了一个ActionListener接口实例

jButton.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent e) {        
    System.out.println("Button Pressed!");
  } 
}); 

使用Lambda表达式改进后

jButton.addActionListener(e -> System.out.println("Button Pressed!"));

也就是我们使用Lambda表达式创建了一个ActionListener接口的实例,再看下ActionListener接口的定义

public interface ActionListener extends EventListener {
  /**
   * Invoked when an action occurs.
   */
  public void actionPerformed(ActionEvent e);
}

只有一个抽象方法,虽然没添加FunctionalInterface注解,但是也符合函数式接口的定义,编译器会认为这是一个函数式接口。
所以,使用Lambda表达式可以创建函数式接口的实例。即Lambda表达式返回的是函数式接口类型。

实际上,函数式接口实例的创建可以有三种方式(参考自FunctionalInterface注解说明):

1.Lambda表达式
2.方法引用
3.构造方法引用

总结

以上就是本文关于Java8简单了解Lambda表达式与函数式接口的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出!

Java8被称作Java史上变化最大的一个版本。其中包含很多重要的新特性,最核心的就是增加…

本文将讨论函数式,它对一种语言或编程方式意味着什么。在回答“Java
8的函数式编程怎么样”之前,我们先看看Java的演变,特别是它的类型系统,我们将看到Java
8的新特性,特别是Lambda表达式如何改变Java的风景,并提供函数式编程风格的主要优势。

Kotlin是一门非研究性的语言,它是一门非常务实的工业级编程语言,它的使命就是帮助程序员们解决实际工程实践中的问题。使用Kotlin

Java程序员们的生活变得更好,Java中的那些空指针错误,浪费时间的冗长的样板代码,啰嗦的语法限制等等,在Kotlin中统统消失。Kotlin
简单务实,语法简洁而强大,安全且表达力强,极富生产力。

函数式编程语言是什么?

函数式编程语言的核心是它以处理数据的方式处理代码。这意味着函数应该是第一等级(First-class)的值,并且能够被赋值给变量,传递给函数等等。

事实上,很多函数式语言比这走得更远,将计算和算法看得比它们操作的数据更重要。其中有些语言想分离程序状态和函数(以一种看起来有点对立的方式,使用面向对象的语言,这通常会将它们联系得更紧密)。

Clojure编程语言就是一个这样的例子,尽管它运行于基于类的Java虚拟机,Clojure的本质是函数式语言,并且在高级语言源程序中不直接公布类和对象(尽管提供了与Java良好的互操作性)。

下面显示的是一个Clojure函数,用于处理日志,是一等公民(First-class
citizen),并且不需要绑定一个类而存在。

(defn build-map-http-entries [log-file]
 (group-by :uri (scan-log-for-http-entries log-file)))

当写在函数中的程序,对给定的输入(不论程序中的其它状态如何)总是返回相同的输出,并且不会产生其它影响,或者改变任何程序状态,这时候函数式编程是最有用的。它们的行为与数学函数相同,有时候把遵循这个标准的函数称为“纯”函数。

纯函数的巨大好处是它们更容易推论,因为它们的操作不依赖于外部状态。函数能够很容易地结合在一起,这在开发者工作流风格中很常见,例如Lisp方言和其它具有强函数传统的语言中很普遍的REPL(Read,
Execute, Print, Loop)风格。

本课程从Kotlin 语言的基础语法讲起,逐步深入介绍了 Kotlin
的扩展函数、一等函数支持、
Lambda表达式、强大的DSL支持、运算符重载、注解与反射等特性,并给出了详尽的综合项目实战讲解:使用
Kotlin + Spring Boot 进行服务端开发,Kotlin 进行 Android
应用程序的开发等,使读者对 Kotlin 编程的整体知识有更为清晰的理解。

非函数式编程语言中的函数式编程

一种语言是不是函数式并不是非此即彼的状态,实际上,语言存在于图谱上。在最末端,基本上是强制函数式编程,通常禁止可变的数据结构。Clojure就是一种不接受可变数据的语言。

不过,也有一些其它语言,通常以函数方式编程,但语言并不强制这一点。Scala就是一个例子,它混和了面向对象和函数式语言。允许函数作为值,例如:

val sqFn = (x: Int) => x * x

同时保留与Java非常接近的类和对象语法。

另一个极端,当然,使用完全非函数式语言进行函数式编程是可能的,例如C语言,只要维持好合适的程序员准则和惯例。

考虑到这一点,函数式编程应该被看作是有两个因素的函数,其中一个与编程语言相关,另一个是用该语言编写的程序:

1)底层编程语言在多大程度上支持,或者强制函数式编程?

2)这个特定的程序如何使用语言提供的函数式特性?它是否避免了非函数式特性,例如可变状态?

Java诞生于1995年,至今已有23年历史。当前最新版本是 Java 10。在 JVM
生态不断发展繁荣的过程中,也诞生了Scala、Groovy、Clojure 等兄弟语言。

Java的一些历史

Java是一种固执己见的语言,它具有很好的可读性,初级程序员很容易上手,具有长期稳定性和可支持性。但这些设计决定也付出了一定的代价:冗长的代码,类型系统与其它语言相比显得缺乏弹性。

然而,Java的类型系统已经在演化,虽然在语言的历史当中相对比较慢。我们来看看这些年来它的一些形式。

Kotlin 也正是 JVM
家族中的优秀一员。Kotlin是一种现代语言(版本1.0于2016年2月发布)。它最初的目的是像Scala那样,优化Java语言的缺陷,提供更加简单实用的编程语言特性,并且解决了性能上的问题,比如编译时间。
JetBrains在这些方面做得非常出色。

Java最初的类型系统

Java最初的类型系统至今已经超过15年了。它简单而清晰,类型包括引用类型和基本类型。类、接口或者数组属于引用类型。

  • 类是Java平台的核心,类是Java平台将会加载、或链接的功能的基本单位,所有要执行的代码都必须驻留于一个类中。
  • 接口不能直接实例化,而是要通过一个实现了接口API的类。
  • 数组可以包含基本类型、类的实例或者其它数组。
  • 基本类型全部由平台定义,程序员不能定义新的基本类型。

从最早开始,Java的类型系统一直坚持很重要的一点,每一种类型都必须有一个可以被引用的名字。这被称为“标明类型(Nominative
typing)”,Java是一种强标明类型语言。

即使是所谓的“匿名内部类”也仍然有类型,程序员必须能引用它们,才能实现那些接口类型:

Runnable r = new Runnable() { public void run() { System.out.println("Hello World!"); } };

换种说法,Java中的每个值要么是基本类型,要么是某个类的实例。

用 Java 开发多年以后,能够尝试一些新的东西真是太棒了。如果您是 Java
开发人员,使用 Kotlin
将会非常自然流畅。如果你是一个Swift开发者,你将会感到似曾相识,比如可空性(Nullability)。
Kotlin语言的特性有:

命名类型(Named Type)的其它选择

其它语言没有这么迷恋命名类型。例如,Java没有这样的Scala概念,一个实现(特定签名的)特定方法的类型。在Scala中,可以这样写:

x : {def bar : String}

记住,Scala在右侧标示变量类型(冒号后面),所以这读起来像是“x是一种类型,它有一个方法bar返回String”。我们能用它来定义类似这样的Scala方法:

def showRefine(x : {def bar : String}) = { print(x.bar) }

然后,如果我们定义一个合适的Scala对象:

object barBell { def bar = "Bell" }

然后调用showRefine(barBell),这就是我们期待的事:

showRefine(barBell) Bell

这是一个精化类型(Refinement
typing)的例子。从动态语言转过来的程序员可能熟悉“鸭子类型(Duck
typing)”。结构精化类型(Structural refinement
typing)是类似的,除了鸭子类型(如果它走起来像鸭子,叫起来像鸭子,就可以把它当作鸭子)是运行时类型,而这些结构精化类型作用于编译时。

在完全支持结构精化类型的语言中,这些精化类型可以用在程序员可能期望的任何地方,例如方法参数的类型。而Java,相反地,不支持这样的类型(除了几个稍微怪异的边缘例子)。

1.简洁大幅减少样板代码量。

Java 5类型系统

Java 5的发布为类型系统带来了三个主要新特性,枚举、注解和泛型。

  • 枚举类型(Enum)在某些方面与类相似,但是它的属性只能是指定数量的实例,每个实例都不同并且在类描述中指定。主要用于“类型安全的常量”,而不是当时普遍使用的小整数常量,枚举构造同时还允许附加的模式,有时候这非常有用。
  • 注解(Annotation)与接口相关,声明注解的关键字是@interface,以@开始表示这是个注解类型。正如名字所建议的,它们用于给Java代码元素做注释,提供附加信息,但不影响其行为。此前,Java曾使用“标记接口(Marker
    interface)”来提供这种元数据的有限形式,但注解被认为更有灵活性。
  • Java泛型提供了参数化类型,其想法是一种类型能扮演其它类型对象的“容器”,无需关心被包含类型的具体细节。装配到容器中的类型通常称为类型参数。

Java
5引入的特性中,枚举和注解为引用类型提供了新的形式,这需要编译器特殊处理,并且有效地从现有类型层级结构分离。

泛型为Java的类型系统增加了显著额外的复杂性,不仅仅因为它们是纯粹的编译时特性,还要求Java开发人员应注意,编译时和运行时的类型系统彼此略有不同。

尽管有这些变化,Java仍然保持标明类型。类型名称现在包括List(读作:“List-of-String”)和Map,
CachedObject>(“Map-of-Class-of-Unknown-Type-to-CachedObject”),但这些仍然是命名的类型,并且每个非基本类型的值仍是某个类的实例。

2.与Java的100%互操作性Kotlin可以直接与Java类交互,反之亦然。这个特性使得我们可以直接重用我们的代码库,并将其迁移到
Kotlin中。由于Java的互操作性几乎无处不在。我们可以直接访问平台API以及现有的代码库,同时仍然享受和使用
Kotlin 的所有强大的现代语言功能。

Java 6和7引入的特性

Java
6基本上是一个性能优化和类库增强的版本。类型系统的唯一变化是扩大注解角色,发布可插拔注解处理功能。这对大多数开发者没有任何影响,Java
6中也没有真正提供可插拔类型系统。

Java 7的类型系统没有重大改变。仅有的一些新特性,看起来都很相似:

  • javac编译器中类型推断的小改进。
  • 签名多态性分派(Signature polymorphic
    dispatch),用于方法句柄(Method handle)的实现细节,而这在Java
    8中又反过来用于实现Lambda表达式。
  • Multi-catch提供了一些“代数数据类型”的小跟踪信息,但这些完全是javac内部的,对最终用户程序员没有任何影响。

3.扩展函数Kotlin 类似于 C# 和 Gosu,
它提供了为现有类提供新功能扩展的能力,而不必从该类继承或使用任何类型的设计模式

Java 8的类型系统

纵观其历史,Java基本上已经由其类型系统所定义。它是语言的核心,并且严格遵守着标明类型。从实际情况来看,Java类型系统在Java
5和7之间没有太大变化。

乍一看,我们可能期望Java
8改变这种状况。毕竟,一个简单的Lambda表达式似乎让我们移除了标明类型:

() -> { System.out.println("Hello World!"); }

这是个没有名字、没有参数的方法,返回void。它仍然是完全静态类型的,但现在是匿名的。

我们逃脱了名词的王国?这真的是Java的一种新的类型形式?

也许不幸的是,答案是否定的。JVM上运行的Java和其它语言,非常严格地限制在类的概念中。类加载是Java平台的安全和验证模式的中心。简单地说,不通过类来表示一种类型,这是非常非常难的。

Java
8没有创建新的类型,而是通过编译器将Lambda表达式自动转换成一个类的实例。这个类由类型推断来决定。例如:

Runnable r = () -> { System.out.println("Hello World!"); };

右侧的Lambda表达式是个有效的Java
8的值,但其类型是根据左侧值推断的,因此它实际上是Runnable类型的值。需要注意的是,如果没有正确地使用Lambda表达式,可能会导致编译器错误。即使是引入了Lambda,Java也没有改变这一点,仍然遵守着标明类型。

4.函数式编程Kotlin
语言一等支持函数式编程,就像Scala一样。具备高阶函数、Lambda
表达式等函数式基本特性。

Java 8的函数式编程怎么样?

最后,让我们回到本文开头提出的问题,“Java 8的函数式编程怎么样?”

Java
8之前,如果开发者想以函数式风格编程,他或她只能使用嵌套类型(通常是匿名内部类)作为函数代码的替代。默认的Collection类库不会为这些代码提供任何方便,可变性的魔咒也始终存在。

Java
8的Lambda表达式没有神奇地转变成函数式语言。相反,它的作用仍是创建强制的强命名类型语言,但有更好的语法支持Lambda表达式函数文本。与此同时,Collection类库也得到了增强,允许Java开发人员开始采用简单的函数式风格(例如filter和map)简化笨重的代码。

Java
8需要引入一些新的类型来表示函数管道的基本构造块,如java.util.function中的Predicate、Function和Consumer接口。这些新增的功能使Java
8能够“稍微函数式编程”,但Java需要用类型来表示它们(并且它们位于工具类包,而不是语言核心),这说明标明类型仍然束缚着Java语言,它离纯粹的Lisp方言或者其它函数式语言是多么的遥远。

除了以上这些,这个函数式语言能量的小集合很可能是所有大多数开发者日常开发所真正需要的。对于高级用户,还有(JVM或其它平台)其它语言,并且毫无疑问,将继续蓬勃发展。

5.默认和命名参数在Kotlin中,您可以为函数中的参数设置一个默认值,并给每个参数一个名称。这有助于编写易读的代码。

6.强大的开发工具支持而由于是JetBrains出品,我们拥有很棒的IDE支持。虽然Java到Kotlin的自动转换并不是100%
OK 的,但它确实是一个非常好的工具。使用 IDEA 的工具转换Java代码为 Kotlin
代码时,可以轻松地重用60%-70%的结果代码,而且修改成本很小。

Kotlin
除了简洁强大的语法特性外,还有实用性非常强的API以及围绕它构建的生态系统。例如:集合类
API、IO 扩展类、反射API 等。同时 Kotlin
社区也提供了丰富的文档和大量的学习资料,还有在线REPL。

1.内容由浅入深、深入浅出,内容讲解符合初学者的认知规律

讲座内容涵盖了Kotlin
语言基础语法、类型系统与可空类型、类与面向对象编程、函数与函数式编程、扩展函数与属性、集合类、泛型、使用
Kotlin 创建
DSL、运算符重载与约定、元编程与注解反射等核心知识点,同时涵盖了 Kotlin +
Spring Boot进行服务端开发、使用Kotlin开发
Android应用程序的实例讲解。从内容结构上非常注重知识的实用性和可操作性。整体结构的描述提纲挈领,内容讲解由浅入深、循序渐进,完全是遵循和尊重了初学者对编程语言学习的的认知规律。

2.案例丰富、图文并茂,以实例引导全程,特别适合初学者阅读本书重点介绍的Kotlin可空类型与安全操作符、
面向对象编程与函数式编程、 Lambda
表达式、扩展函数与创建DSL等Kotlin语言的特性时都给出了丰富的代码示例,在介绍集合类与泛型的时候给出详尽简单易懂的图示,一看就懂。代码注释案例丰富,内容讲解生动有趣,图文并茂,这就使得整个学习Kotlin
语言的过程变得简单有趣起来。的实例讲解都是全新的案例。

  • 第1讲 Kotlin是什么

本讲介绍Kotlin 编程语言的基本特性、编程哲学、学习工具、为什么要学
Kotlin、 JVM语言生态等内容。

  • 第2讲 Kotlin 语法基础本讲介绍 Kotlin
    语言的语法基础知识。主要内容包括变量和标识符、关键字与修饰符、流程控制语句、操作符重载与优先级、包声明等内容。

  • 第3讲 类型系统与可空类型本讲介绍Kotlin
    的类型系统、可空类型、安全操作符、特殊类型、类型检测与类型转换等内容。

  • 第4讲
    类与面向对象编程本讲介绍Kotlin面向对象编程的特性。主要内容包括声明类、抽象类与接口、object对象、数据类、注解、枚举、内部类等内容。

  • 第5讲
    函数与函数式编程本讲介绍Kotlin函数式编程特性。主要内容包括声明函数、Lambda表达式、高阶函数等内容。

  • 第6讲 扩展函数与属性本讲介绍 Kotlin扩展函数与属性的特性。

  • 第7讲 集合类本讲介绍
    Kotlin集合类。主要内容包括常用的3种集合类、不可变集合类、创建集合类、遍历集合中的元素、映射函数、过滤函数、排序函数、元素去重等内容。

  • 第8讲 泛型本讲介绍
    Kotlin的泛型。主要内容包括为何引入泛型、泛型接口、泛型类、泛型函数、类型上界、协变与逆变、out
    T与in T、类型擦除等内容。

  • 第9讲 文件IO操作、正则表达式与多线程本讲介绍 Kotlin
    语言的文件IO操作、正则表达式与多线程等相关内容。

  • 第10讲 使用 Kotlin 创建 DSL本讲介绍怎样使用 Kotlin 语言创建 DSL
    。主要内容包括什么是DSL、Kotlin的DSL特性支持,同时给出实现一个HTTP
    AJAX请求的DSL实例讲解。

  • 第11讲 运算符重载与约定本讲介绍
    Kotlin运算符重载与约定。主要内容包括什么是运算符重载、重载二元算术运算符、自增自减一元运算符、比较运算符、计算赋值运算符等内容。

  • 第12讲 元编程与注解、反射本讲介绍
    Kotlin元编程的相关内容。主要内容包括元编程简介、声明注解与处理注解、反射API、
    类和函数的引用、绑定函数和属性引用、使用反射获取泛型信息等内容。

  • 第13讲 Kotlin 集成 Spring Boot 服务端开发本讲介绍Kotlin 集成 Spring
    Boot 服务端开发。给出了一个图片爬虫的Web应用实例讲解。

  • 第14讲 使用Kotlin 进行 Android 开发本讲介绍使用Kotlin 进行 Android
    开发。给出了一个Kotlin 开发一个电影指南Android应用程序的实例讲解。

  • 编程初学者
  • Android 程序员
  • Java 程序员
  • 需要Kotlin 编程入门工具书的人员
  • 对编程有兴趣爱好的各类人员

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring
Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

澳门新葡萄京官网注册 1开发者社区
QRCode.jpg

发表评论

电子邮件地址不会被公开。 必填项已用*标注