javaparser lambda,java为什么要有lambda表达式
javaparser lambda,java为什么要有lambda表达式详细介绍
本文目录一览: java lambda
java的lambda表达式是什么?下面就一起来了解一下吧~
Lambda 表达式被熟称为:闭包,是推动 Java 8 发布的一个新特性,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法
lambda 表达式的语法格式如下:
(parameters)?->?expression或(parameters)?->{?statements;?}以下是lambda表达式的重要特征:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
Lambda 表达式实例
Lambda?表达式的简单例子: //?1.?不需要参数,返回值为?5??()?->?5?? ??//?2.?接收一个参数(数字类型),返回其2倍的值??x?->?2?*?x?? ?? //?3.?接受2个参数(数字),并返回他们的差值??(x,?y)?->?x?–?y?? ?? //?4.?接收2个int型整数,返回他们的和??(int?x,?int?y)?->?x?+?y?? ?? //?5.?接受一个?string?对象,并在控制台打印,不返回任何值(看起来像是返回void)??(String?s)?->?System.out.print(s)
在 Java8Tester.java 文件输入以下代码:
Java8Tester.java?文件 public?class?Java8Tester?{ ???public?static?void?main(String?args[]){ ??????Java8Tester?tester?=?new?Java8Tester();???????? ??????//?类型声明 ??????MathOperation?addition?=?(int?a,?int?b)?->?a?+?b;???????? ??????//?不用类型声明 ??????MathOperation?subtraction?=?(a,?b)?->?a?-?b;???????? ??????//?大括号中的返回语句 ??????MathOperation?multiplication?=?(int?a,?int?b)?->?{?return?a?*?b;?};???????? ??????//?没有大括号及返回语句 ??????MathOperation?division?=?(int?a,?int?b)?->?a?/?b;???????? ??????System.out.println("10?+?5?=?"?+?tester.operate(10,?5,?addition));??????System.out.println("10?-?5?=?"?+?tester.operate(10,?5,?subtraction));??????System.out.println("10?x?5?=?"?+?tester.operate(10,?5,?multiplication));??????System.out.println("10?/?5?=?"?+?tester.operate(10,?5,?division));???????? ??????//?不用括号 ??????GreetingService?greetService1?=?message?->??????System.out.println("Hello?"?+?message);???????? ??????//?用括号 ??????GreetingService?greetService2?=?(message)?->??????System.out.println("Hello?"?+?message);???????? ??????greetService1.sayMessage("Runoob");??????greetService2.sayMessage("Google");???} ???? ???interface?MathOperation?{ ??????int?operation(int?a,?int?b);???} ???? ???interface?GreetingService?{ ??????void?sayMessage(String?message);???} ???? ???private?int?operate(int?a,?int?b,?MathOperation?mathOperation){ ??????return?mathOperation.operation(a,?b);???}}
执行以上脚本,输出结果为:
$?javac?Java8Tester.java $?java?Java8Tester10?+?5?=?1510?-?5?=?510?x?5?=?5010?/?5?=?2Hello?RunoobHello?Google
使用 Lambda 表达式需要注意以下两点:
Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
java lambda表达式
java lambda表达式如下:
Lambda 表达式是一个匿名函数(对于 Java 而言并不很准确,但这里我们不纠结这个问题)。简单来说,这是一种没有声明的方法,即没有访问修饰符,返回值声明和名称。
在仅使用一次方法的地方特别有用,方法定义很短。它为我们节省了,如包含类声明和编写单独方法的工作。
Java 中的 Lambda 表达式通常使用语法是 (argument) -> (body)
Lambda 表达式的结构
Lambda 表达式的结构:
Lambda 表达式可以具有零个,一个或多个参数。
可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a)。
参数用小括号括起来,用逗号分隔。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c)。
空括号用于表示一组空的参数。例如 () -> 42。
当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a。
Lambda 表达式的正文可以包含零条,一条或多条语句。
如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。
java parser
java parser是什么,让我们一起了解一下?
Java Parser是基于JavaCC作为Java语言词法解析的工具,支持Java语言生成AST(Abstract Syntax Tree抽象语法树),在AST基础上进行类型推断分析,支持修改AST从而生成新的Java文件内容,支持从Java 1.0到14所有的版本的AST解析。
AST结构允许以一种简单的编程方式使用Java代码。可以专门操作Java文件,使用起来也更简单。它提供了一种方便的机制,可以使用我们称为“访客支持”的功能来导航树。这使开发人员能够专注于在源代码中识别有趣的模式,而不必编写费力的树遍历代码。该库的最终主要功能是能够操纵源代码的基础结构。然后可以将其写入文件,为开发人员提供构建自己的代码生成软件的便利。
那么我们使用JavaParser theere时,总是希望进行很多操作。
例如,我们希望对整个项目进行操作,因此在给定目录的情况下,我们将探索所有Java文件。 此类应有助于完成此任务:
package?me.tomassetti.support; import?java.io.File; public?class?DirExplorer?{ public?interface?FileHandler?{ void?handle(int?level,?String?path,?File?file); } public?interface?Filter?{ boolean?interested(int?level,?String?path,?File?file); } private?FileHandler?fileHandler; private?Filter?filter; public?DirExplorer(Filter?filter,?FileHandler?fileHandler)?{ this.filter?=?filter; this.fileHandler?=?fileHandler; } public?void?explore(File?root)?{ explore(0,?"",?root); } private?void?explore(int?level,?String?path,?File?file)?{ if?(file.isDirectory())?{ for?(File?child?:?file.listFiles())?{ explore(level?+?1,?path?+?"/"?+?child.getName(),?child); } }?else?{ if?(filter.interested(level,?path,?file))?{ fileHandler.handle(level,?path,?file); } } } }
对于每个Java文件,我们首先要为每个Java文件构建一个抽象语法树(AST),然后对其进行导航。 这样做有两种主要策略:
1、使用访客:要在特定类型的AST节点上进行操作时,这是正确的策略。
2、使用递归迭代器:这允许处理所有类型的节点。
java lambda表达式是什么?
lambda表达式是JAVA8中提供的一种新的特性,它支持JAVA也能进行简单的“函数式编程”。它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
函数式接口:
这个是理解Lambda表达式的重点,也是产生lambda表达式的“母体”,这里我们引用一个比较容易理解的说法:函数式接口是 一个只有一个抽象方法(不包含object中的方法)的接口。
这个需要说明一点,就是在Java中任何一个对象都来自Object 所有接口中自然会继承自Object中的方法,但在判断是否是函数式接口的时候要排除Object中的方法。
探索Java语言与JVM中的Lambda表达式
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
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
myStrings) {
List
out = new ArrayList<>()
for (String s: myStrings) {
if (s equalsIgnoreCase(possible))
out add(s)
}
log(out)
}
而在即将发布的Java SE 中 我们使用Predicate以及Collections类中一个新的助手方法(过滤器)就可写出更为紧凑的程序 如清单 所示
清单
public void printMatchedStrings() {
Predicate
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表达式 所以咱也别啰嗦了…
lishixinzhi/Article/program/Java/hx/201311/26388
java8 lambda 表达式是个什么
为了支持函数式编程,Java 8引入了Lambda表达式.
在Java 8中采用的是内部类来实现Lambda表达式.具体实现代码,可以通过debug看, 同时通过字节码查看工具及反编译工具来验证.
自从lambda表达式成为Java语言的一部分之后,Java集合(Collections)API就面临着大幅变化。而JSR 355(规定了Java lambda表达式的标准)的正式启用更是使得Java集合API变的过时不堪。
尽管我们可以从头实现一个新的集合框架(比如“Collection II”),但取代现有的集合框架是一项非常艰难的工作,因为集合接口渗透了Java生态系统的每个角落,将它们一一换成新类库需要相当长的时间。因此,我们决定采取演化的策略(而非推倒重来)以改进集合API: 为现有的接口(例如Collection,List和Stream)增加扩展方法;
在类库中增加新的流(stream,即java.util.stream.Stream)抽象以便进行聚集(aggregation)操作;
改造现有的类型使之可以提供流视图(stream view);
改造现有的类型使之可以容易的使用新的编程模式,这样用户就不必抛弃使用以久的类库,例如ArrayList和HashMap
(当然这并不是说集合API会常驻永存,毕竟集合API在设计之初并没有考虑到lambda表达式。我们可能会在未来的JDK中添加一个更现代的集合类库)。
除了上面的改进,还有一项重要工作就是提供更加易用的并行(Parallelism)库。尽管Java平台已经对并行和并发提供了强有力的支持,然而开发者在实际工作(将串行代码并行化)中仍然会碰到很多问题。因此,我们希望Java类库能够既便于编写串行代码也便于编写并行代码,因此我们把编程的重点从具体执行细节(how computation should be formed)转移到抽象执行步骤(what computation should be perfomed)。
Lambda表达式的Java表达式
Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。下面这个例子就是使用Lambda语法来代替匿名的内部类,代码不仅简洁,而且还可读。没有使用Lambda的老方法: button.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent ae){System.out.println(Actiondetected);}});使用Lambda: button.addActionListener(()->{System.out.println(Actiondetected);});让我们来看一个更明显的例子。不采用Lambda的老方法: Runnable runnable1=new Runnable(){@Overridepublic void run(){System.out.println(RunningwithoutLambda);}};使用Lambda: Runnable runnable2=()->{System.out.println(RunningfromLambda);};正如你所看到的,使用Lambda表达式不仅让代码变的简单、而且可读、最重要的是代码量也随之减少很多。然而,在某种程度上,这些功能在Scala等这些JVM语言里已经被广泛使用。并不奇怪,Scala社区是难以置信的,因为许多Java 8里的内容看起来就像是从Scala里搬过来的。在某种程度上,Java 8的语法要比Scala的更详细但不是很清晰,但这并不能说明什么,如果可以,它可能会像Scala那样构建Lambda表达式。一方面,如果Java继续围绕Lambda来发展和实现Scala都已经实现的功能,那么可能就不需要Scala了。另一方面,如果它只提供一些核心的功能,例如帮助匿名内部类,那么Scala和其他语言将会继续茁壮成长,并且有可能会凌驾于Java之上。其实这才是最好的结果,有竞争才有进步,其它语言继续发展和成长,并且无需担心是否会过时。
java为什么要有lambda表达式
这应该是在外国使用的较多吧
~
Lambda表达式是JDK8(也就是JDK1.8)最重要的新增功能。
我认为Lambda表达式的主要功能是利用”参数列 ->方法本体“的语法省略重复声明变量类型和不想费神去命名一个方法去调用另一个方法的表达式。
比如子JDK8之前
public interface IntegerFunction {
Integer apply(Integer i);
}
你可以使用匿名类来实现IntegerFunction 的实例。
IntegerFunction integerFunction=new IntegerFunction(){
public Integer apply(Integer i) {
return i*2;
}
}
你会看到信息重复了,声明integerFunction变量是已经告知是IntegerFunction 类型了,而建立IntegerFunction 实例的语法又写了一次。若用JDK8的Lambda表达式。可以直接改写为:
IntegerFunction integerFunction=(Integer i) -> i*2;
这些只是简略的介绍Lambda表达式的基本语法,如果你想更加全面的了解Lambda表达式的完整应用。可以看一些书籍参考一下其中更详细的介绍。比如林信良的《java学习笔记 jdk8》中的第九章和第十二章就有介绍。
Java8新特性lambda表达式有什么用
随着编程语言生态系统的气候不断变化以及技术的革新,经历20余年的发展,Java逐渐演变成长为Java8。相比之前只是单纯的面向对象编程语言,Java8增加了很多新特性。
Java 8对于程序员的主要好处在于它提供了更多的编程工具和概念,能以更为简洁、更易于维护的方式解决新的或现有的编程问题。在Java 8中有两个著名的改进:一个是Lambda表达式,一个是Stream。
Lambda表达式是什么?
Lambda表达式,也可称为闭包,它允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用Lambda表达式可以使代码变的更加简洁紧凑,Lambda表达式的语法格式:
(parameters) -> expression或(parameters) ->{ statements; }
Lambda表达式的重要特征:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
使用Lambda表达式需要注意以下两点:
Lambda表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
Lambda表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
Stream是什么?
Stream就是一个流,它的主要作用就是对集合数据进行查找过滤等操作。Java 8中的 Stream是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作(bulk data operation)。
对于基本数值型,目前有三种对应的包装类型Stream:IntStream、LongStream、DoubleStream。当然我们也可以用Stream
、Stream
>、Stream
,但是boxing和 unboxing会很耗时,所以特别为这三种基本数值型提供了对应的Stream。
Java 8中还没有提供其它数值型Stream,因为这将导致扩增的内容较多。而常规的数值型聚合运算可以通过上面三种Stream进行。
Stream上的操作分为两类:中间操作和结束操作。中间操作只是一种标记,只有结束操作才会触发实际计算。
中间操作又可以分为无状态的(Stateless)和有状态的(Stateful),无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果。
结束操作又可以分为短路操作和非短路操作,短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。
想要永远处于优势地位,就要不断的完善自身、更新技术。
Java 8终于引进了lambda表达式,这标志着Java往函数式编程又迈进了一小步。
在Java 8以前的代码中,为了实现带一个方法的接口,往往需要定义一个匿名类并复写接口方法,代码显得很臃肿。比如常见的Comparator接口:
String[] oldWay = "Improving code with Lambda expressions in Java 8".split(" ");Arrays.sort(oldWay, new Comparator
() { @Override public int compare(String s1, String s2) { // 忽略大小写排序: return s1.toLowerCase().compareTo(s2.toLowerCase()); }});System.out.println(String.join(", ", oldWay));对于只有一个方法的接口,在Java 8中,现在可以把它视为一个函数,用lambda表示式简化如下:
String[] newWay = "Improving code with Lambda expressions in Java 8".split(" ");Arrays.sort(newWay, (s1, s2) -> { return s1.toLowerCase().compareTo(s2.toLowerCase());});System.out.println(String.join(", ", newWay));Java 8没有引入新的关键字lambda,而是用()->{}这个奇怪的符号表示lambda函数。函数类型不需要申明,可以由接口的方法签名自动推导出来,对于上面的lambda函数:
(s1, s2) -> { return s1.toLowerCase().compareTo(s2.toLowerCase());});参数由Comparator
自动推导出String类型,返回值也必须符合接口的方法签名。
实际上,lambda表达式最终也被编译为一个实现类,不过语法上做了简化。
对于Java自带的标准库里的大量单一方法接口,很多都已经标记为@FunctionalInterface,表明该接口可以作为函数使用。
以Runnable接口为例,很多时候干活的代码还没有定义class的代码多,现在可以用lambda实现:
public static void main(String[] args) { // old way: Runnable oldRunnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": Old Runnable"); } }; Runnable newRunnable = () -> { System.out.println(Thread.currentThread().getName() + ": New Lambda Runnable"); }; new Thread(oldRunnable).start(); new Thread(newRunnable).start();}在未来的Java代码中,会出现越来越多的()->{}表达式。
为什么要使用lambda表达式?原来如此,涨知识了
先看几段Java8以前经常会遇到的代码:
创建线程并启动
比较数组
给按钮添加单击事件
对于这三段代码,我们已经司空见惯了。
Java复杂冗余的代码实现一直被程序员所诟病,好在随着JVM平台语言Scala的兴起以及函数式编程风格的风靡,让Oracle在Java的第8个系列版本中进行了革命性的变化,推出了一系列函数式编程风格的语法特性,比如Lambda表达式以及Stream。
如果采用Lambda表达式,上面三段代码的实现将会变得极为简洁。
创建线程并启动(采用Lambda版本)
比较数组(采用Lambda版本)
给按钮添加单击事件(采用Lambda版本)
格式:(参数) -> 表达式
其中:
一个参数
多个参数
0个参数
表达式块
在Java8中新增加了一个注解: [@FunctionalInterface],函数式接口。
什么是函数式接口呢?它包含了以下特征:
Lambda表达式的本质就是函数式接口的匿名实现。只是把原有的接口实现方式用一种更像函数式编程的语法表示出来。
Java8的java.util.function包已经内置了大量的函数式接口,如下所示:
从中可以看出:
以下是一个综合的例子:
如果觉得这些内置函数式接口还不够用的话,还可以自定义自己的函数式接口,以满足更多的需求。
如果Lambda表达式已经有实现的方法了,则可以用方法引用进行简化。 方法引用的语法如下:
这样前面提到的Lambda表达式:
则可以替换为:
另一个例子:
可以替换为:
注意:方法名后面是不能带参数的! 可以写成System.out::println,但不能写成System.out::println(“hello”)
如果能获取到本实例的this参数,则可以直接用this::实例方法进行访问,对于父类指定方法,用super::实例方法进行访问。
下面是一个例子:
构造器引用和方法引用类似,只不过函数接口返回实例对象或者数组。 构造器引用的语法如下:
举个例子:
其中的labels.stream().map(Button::new)相当于 labels.stream().map(label->new Button(label))
再看个数组类型的构造器引用的例子:
把Stream直接转成了数组类型,这里用Button[]::new来标示数组类型。
先看一段代码:
一个lambda表达式一般由以下三部分组成:
参数和表达式好理解。那自由变量是什么呢? 它就是在lambda表达式中引用的外部变量,比如上例中的text和count变量。
如果熟悉函数式编程的同学会发现,Lambda表达式其实就是”闭包”(closure)。只是Java8并未叫这个名字。 对于自由变量,如果Lambda表达式需要引用,是不允许发生修改的。
比如下面的代码:
先说说为什么要在Java8接口中新增默认方法吧。
比如Collection接口的设计人员针对集合的遍历新增加了一个forEach()方法,用它可以更简洁的遍历集合。 比如:
但如果在接口中新增方法,按照传统的方法,Collection接口的自定义实现类都要实现forEach()方法,这对广大已有实现来说是无法接受的。
于是Java8的设计人员就想出了这个办法:在接口中新增加一个方法类型,叫默认方法,可以提供默认的方法实现,这样实现类如果不实现方法的话,可以默认使用默认方法中的实现。
一个使用例子:
默认方法的加入,可以替代之前经典的接口和抽象类的设计方式,统一把抽象方法和默认实现都放在一个接口中定义。这估计也是从Scala的Trait偷师来的技能吧。
除了默认方法,Java8还支持在接口中定义静态方法以及实现。
比如Java8之前,对于Path接口,一般都会定义一个Paths的工具类,通过静态方法实现接口的辅助方法。
接口中有了静态方法就好办了, 统一在一个接口中搞定!虽然这看上去破坏了接口原有的设计思想。
这样Paths类就没什么意义了~
使用Lambda表达式后可以大幅减少冗余的模板式代码,使把更多注意力放在业务逻辑上,而不是复制一堆重复代码, 除非你在一个用代码行数来衡量工作量的公司,你觉得呢?