知识大全 探索Java语言与JVM中的Lambda表达式

Posted 表达式

篇首语:常识是人类的守护神。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 探索Java语言与JVM中的Lambda表达式相关的知识,希望对你有一定的参考价值。

探索Java语言与JVM中的Lambda表达式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  Lambda表达式是自Java SE 引入泛型以来最重大的Java语言新特性 本文是 年度最后一期Java Magazine中的一篇文章 它介绍了Lamdba的设计初衷 应用场景与基本语法

  Lambda表达式 这个名字由该项目的专家组选定 描述了一种新的函数式编程结构 这个即将出现在Java SE 中的新特性正被大家急切地等待着 有时你也会听到人们使用诸如闭包 函数直接量 匿名函数 及SAM(Single Abstract Method)这样的术语 其中一些术语彼此之间会有一些细微的不同 但基本上它们都指代相同的功能

  虽然一开始会觉得Lambda表达式看起来很陌生 但很容易就能掌握它 而且为了编写可完全利用现代多核CPU的应用程序 掌握Lambda表达式是至关重要的

  需要牢记的一个关键概念就是 Lambda表达式是一个很小且能被当作数据进行传递的函数 需要掌握的第二个概念就是 理解集合对象是如何在内部进行遍历的 这种遍历不同于当前已有的外部顺序化遍历

  在本文中 我们将向你展示Lambda表达式背后的动因 应用示例 当然 还有它的语法

  为什么你需要Lambda表达式

  程序员需要Lambda表达式的原因主要有三个

   更紧凑的代码

   通过提供额外的功能对方法的功能进行修改的能力

   更好地支持多核处理

  更紧凑的代码

  Lambda表达式以一种简洁的方式去实现仅有一个方法的Java类

  例如 如果代码中有大量的匿名内部类 诸如用于UI应用中的监听器与处理器实现 以及用于并发应用中的Callable与Runnable实现 在使用了Lambda表达式之后 将使代码变得非常短 且更易于理解

  修改方法的能力

  有时 方法不具备我们想要的一些功能 例如 Collection接口中的contains()方法只有当传入的对象确实存在于该集合对象中时才会返回true 但我们无法去干预该方法的功能 比如 若使用不同的大小写方案也可以认为正在查找的字符串存在于这个集合对象中 我们希望此时contains()方法也能返回true

  简单点儿说 我们所期望做的就是 将我们自己的新代码传入 已有的方法中 然后再调用这个传进去的代码 Lambda表达式提供了一种很好的途径来代表这种被传入已有方法且应该还会被回调的代码

  更好地支持多核处理

  当今的CPU具备多个内核 这就意味着 多线程程序能够真正地被并行执行 这完全不同于在单核CPU中使用时间共享这种方式 通过在Java中支持函数式编程语法 Lambda表达式能帮助你编写简单的代码去高效地应用这些CPU内核

  例如 你能够并行地操控大集合对象 通过利用并行编程模式 如过滤 映射和化简(后面将会很快接触到这些模式) 就可使用到CPU中所有可用的硬件线程

  Lambda表达式概览

  在前面提到的使用不同大小写方案查找字符串的例子中 我们想做的就是把方法toLowerCase()的表示法作为第二个参数传入到contains()方法中 为此需要做如下的工作

   找到一种途径 可将代码片断当作一个值(某种对象)进行处理

   找到一种途径 将上述代码片断传递给一个变量

  换言之 我们需要将一个程序逻辑包装到某个对象中 并且该对象可以被进行传递 为了说的更具体点儿 让我们来看两个基本的Lambda表达式的例子 它们都是可以被现有的Java代码进行替换的

  过滤

  你可能想传递的代码片断可能就是过滤器了 这是一个很好的示例 例如 假设你正在使用(Java SE 预览版中的)java io FileFilter去确定目录隶属于给定的路径 如清单 所示

  清单

  File dir = new File( /an/interesting/location/ )

  FileFilter directoryFilter = new FileFilter()

  public boolean accept(File file)

  return file isDirectory()

  

  ;

  File[] directories = dir listFiles(directoryFilter)

  在使用Lambda表达式之后 代码会得到极大的简化 如清单 所示

  清单

  File dir = new File( /an/interesting/location/ )

  FileFilter directoryFilter = (File f) > f isDirectory()

  File[] directories = dir listFiles(directoryFilter)

  赋值表达式的左边会推导出类型(FileFilter) 右边则看起来像FileFilter接口中accept()方法的一个缩小版 该方法会接受一个File对象 在判定f isDirectory()之后返回一个布尔值

  实际上 由于Lambda表达式利用了类型推导 基于后面的工作原理 我们还可以进一步简化上述代码 编译器知道FileFilter只有唯一的方法accept() 所以它必定是该方法的实现 我们还知 accept()方法只需要一个File类型的参数 因此 f必定是File类型的 如清单 所示

  清单

  File dir = new File( /an/interesting/location/ )

  File[] directories = dir listFiles(f > f isDirectory())

  你可以看到 使用Lambda表达式会大幅降低模板代码的数量

  一旦你习惯于使用Lambda表达式 它会使逻辑流程变得非常易于阅读 在达到这一目的的关键方法之一就是将过滤逻辑置于使用该逻辑的方法的侧边

  事件处理器

  UI程序是另一个大量使用匿名内部类的领域 让我们将一个点击监听器赋给一个按钮 如清单 所示

  清单

  Button button = new Button()

  button addActionListener(new ActionListener()

  public void actionPerformed(ActionEvent e)

  ui showSomething()

  

  )

  这多么代码无非是说 当点击该按钮时 调用该方法 使用Lambda表达式就可写出如清单 所示的代码

  清单

  ActionListener listener = event > ui showSomething() ;

  button addActionListener(listener)

  该监听器在必要时可被复用 但如果它仅需被使用一次 清单 中的代码则考虑了一种很好的方式

  清单

  button addActionListener(event > ui showSomething() )

  在这个例子中 这种使用额外花括号的语法有些古怪 但这是必须的 因为actionPerformed()方法返回的是void 后面我们会看到与此有关的更多内容

  现在让我们转而关注Lambda表达式在编写处理集合对象的新式代码中所扮演的角色 尤其是当针对两种编程风格 外部遍历与内部遍历 之间的转换的时候

  外部遍历 vs 内部遍历

  到目前为止 处理Java集合对象的标准方式是通过外部遍历 之所以称其为外部遍历 是因为要使用集合对象外部的控制流程去遍历集合所包含的元素 这种传统的处理集合的方式为多数Java程序员所熟知 尽管他们并不知道或不使用外部遍历这个术语

  如清单 所示 Java语言为增强的for循环构造了一个外部迭代器 并使用这个迭代器去遍历集合对象

  清单

  List<String> myStrings = getMyStrings()

  for (String myString : myStrings)

  if (ntains(possible))

  System out println(myString + contains + possible)

  

  

  使用这种方法 集合类代表着全部元素的一个 整体 视图 并且该集合对象还能支持对任意元素的随机访问 程序员可能会有这种需求

  基于这种观点 可通过调用iterator()方法去遍历集合对象 该方法将返回集合元素类型的迭代器 该迭代器是针对同一集合对象的更具限制性的视图 它没有为随机访问暴露任何接口 相反 它纯粹是为了顺序地访问集合元素而设计的 这种顺序本性使得当你试图并发地访问集合对象时就会造成臭名昭著的ConcurrentModificationException

  另一种可选的方案就是要求集合对象要能够在内部管理迭代器(或循环) 这种方案就是内部遍历 当使用Lambda表达式时会优先选择内部遍历

  除了新的Lambda表达式语法以外 Lambda项目还包括一个经过大幅升级的集合框架类库 这次升级的目的是为了能更易于编写使用内部遍历的代码 以支持一系列众所周知的函数式编程典范

  使用Lambda的函数式编程

  曾经 大多数开发者发现他们需要集合能够执行如下一种或几种操作

   创建一个新的集合对象 但要过滤掉不符合条件的元素

   对集合中的元素逐一进行转化 并使用转化后的集合

   创建集合中所有元素的某个属性的总体值 例如 合计值与平均值 这样的任务(分别称之为过滤 映射和化简)具有共通的要点 它们都需要处理集合中的每个元素

  程序无论是判定某个元素是否存在 或是判断元素是否符合某个条件(过滤) 或是将元素转化成新元素并生成新集合(映射) 或是计算总体值(化简) 关键原理就是 程序必须处理到集合中的每个元素

  这就暗示我们需要一种简单的途径去表示用于内部遍历的程序 幸运地是 Java SE 为此类表示法提供了构建语句块

  支持基本函数式编程的Java SE 类

  Java SE 中的一些类意在被用于实现前述的函数式典范 这些类包括Predicate Mapper和Block 当然 还有其它的一些类 它们都在一个新的java util functions包中

  看看Predicate类的更多细节 该类常被用于实现过滤算法 将它作用于一个集合 以返回一个包含有符合谓语条件元素的新集合 何为谓语 有很多种解释 Java SE 认为谓语是一个依据其变量的值来判定真或假的方法

  再考虑一下我们之前看过的一个例子 给定一个字符串的集合 我们想判定它是否包含有指定的字符串 但希望字符串的比较是大小写不敏感的

  在Java SE 中 我们将需要使用外部遍历 其代码将如清单 所示

  清单

  public void printMatchedStrings(List<String> myStrings)

  List<String> out = new ArrayList<>()

  for (String s: myStrings)

  if (s equalsIgnoreCase(possible))

  out add(s)

  

  log(out)

  

  而在即将发布的Java SE 中 我们使用Predicate以及Collections类中一个新的助手方法(过滤器)就可写出更为紧凑的程序 如清单 所示

  清单

  public void printMatchedStrings()

  Predicate<String> matched = s > s equalsIgnoreCase(possible)

  log(myStrings filter(matched))

  

  事实上 如果使用更为通用的函数式编程风格 你只需要写一行代码 如清单 所示

  清单

  public void printMatchedStrings()

  log(myStrings filter(s > s equalsIgnoreCase(possible)))

  

  如你所见 代码依然非常的易读 并且我们也体会到了使用内部遍历的好处

  最后 让我们讨论一下Lambda表达式语法的更多细节

  Lambda表达式的语法规则

  Lambda表达式的基本格式是以一个可被接受的参数列表开头 以一些代码(称之为表达式体/body)结尾 并以箭头( >)将前两者分隔开

  注意 Lambda表达式的语法仍可能会面临改变 但在撰写本文的时候 下面示例中所展示的语法是能够正常工作的

  Lambda表达式非常倚重类型推导 与Java的其它语法相比 这显得极其不同寻常

  让我们进一步考虑之前已经看过的一个示例(请见清单 ) 如果看看ActionListener的定义 可以发现它只有一个方法(请见清单 )

  清单

  ActionListener listener = event > ui showSomething() ;

  清单

  public interface ActionListener

  public void actionPerformed(ActionEvent event)

  

  所以 在清单 右侧的Lambda表达式 能够很容易地理解为 这是针对仅声明单个方法的接口的方法定义 注意 仍然必须要遵守Java静态类型的一般规则 这是使类型推导能正确工作的唯一途径

  据此可以发现 使用Lambda表达式可以将先前所写的匿名内部类代码转换更紧凑的代码

  还需要意识到有另一个怪异的语法 让我们再回顾下上述示例 如清单 所示

  清单

  FileFilter directoryFilter = (File f) > f isDirectory()

  仅一瞥之 它看起来与ActionListener的示例相似 但让我们看看FileFilter接口的定义(请见清单 ) accept()方法会返回一个布尔值 但并没有一个显式的返回语句 相反 该返回值的类型是从Lambda表达式中推导出来的

  清单

  public interface FileFilter

  public boolean accept(File pathname)

  

  这就能解释 当方法返回类型为void时 为什么要进行特别处理了 对于这种情形 Lambda表达式会使用一对额外的小括号去包住代码部分(表达式体/body) 若没有这种怪异的语法 类型推导将无法正常工作 但你要明白 这一语法可能会被改变

  Lambda表达式的表达式体可以包含多条语句 对于这种情形 表达式体需要被小括号包围住 但 被推导出的返回类型 这种语法将不启作用 那么返回类型关键字就必不可少

  最后还需要提醒你的是 当前 IDE似乎还不支持Lambda语法 所以当你第一次尝试Lambda表达式时 必须要格外注意javac编译器抛出的任何警告

  结论

  Lambda表达式是自Java SE 引入泛型以来最重大的Java语言新特性 应用得当 Lambda表达式可使你写出简洁的代码 为已有方法增加额外的功能 并能更好地适应多核处理器 到目前为止 我们能肯定的是 你正急切地想去尝试Lambda表达式 所以咱也别啰嗦了…

cha138/Article/program/Java/hx/201311/26388

相关参考

知识大全 JAVA语言编程中如何使用表达式

JAVA语言编程中如何使用表达式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  JAVA正则表达

知识大全 Lambda表达式的一般应用

.Net基础:Lambda表达式的一般应用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在Li

知识大全 Lambda表达式的一般应用

Lambda表达式的一般应用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在List中实现Fi

知识大全 Linq入门演练:lambda表达式

Linq入门演练:lambda表达式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  今天大家一同

知识大全 64位计算中的Java虚拟机(JVM)性能测试

64位计算中的Java虚拟机(JVM)性能测试  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  随

知识大全 浅谈如何使用Lambda 表达式做抽象代表

浅谈如何使用Lambda表达式做抽象代表  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Lamb

知识大全 浅谈如何使用Lambda表达式做抽象代表

浅谈如何使用Lambda表达式做抽象代表  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Lamb

知识大全 .Net基础Lambda表达式的一般应用教程

.Net基础Lambda表达式的一般应用教程  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在L

知识大全 JVM中的栈和局部变量

深入Java核心:JVM中的栈和局部变量  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Java

知识大全 JVM,内存回收及其他

深入探索Java工作原理:JVM,内存回收及其他  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!