Java8实战-总结2

news/2025/3/29 19:30:25/

Java8实战-总结2

  • 基础知识
    • 方法和Lambda
      • 传递代码:一个例子
      • 从传递方法到Lambda

基础知识

方法和Lambda

Scala和Groovy等语言的实践已经证明,让方法等概念作为一等值可以扩充程序员的工具库,从而让编程变得更容易。一旦程序员熟悉了这个强大的功能,就再也不愿意使用没有这一功能的语言了。因此,Java 8的设计者决定允许方法作为值,让编程更轻松。此外,让方法作为值也构成了其他若干Java 8功能(如Stream)的基础。

介绍的Java 8的第一个新功能是方法引用。比方说,想要筛选一个目录中的所有隐藏文件。需要编写一个方法,然后给它一个File,它就会告诉你文件是不是隐藏的。幸好,File类里面有一个叫作isHidden的方法。可以把它看作一个函数,接受一个File,返回一个布尔值。但要用它做筛选,需要把它包在一个FileFilter对象里,然后传递给File.listFiles方法,如下所示:

	File[] hiddenFiles = new File(".").listFiles(new FileFilter() {public boolean accept(File file) {return file.isHidden();}});

虽然只有三行,但这三行可真够绕的。“非得这样不可吗?”已经有一个方法isHidden可以使用,为什么非得把它包在一个啰嗦的FileFilter类里面再实例化呢?因为在Java 8之前必须这么做!
如今在Java 8里,可以把代码重写成这个样子:

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

已经有了函数isHidden,因此只需用Java 8的方法引用::语法(即“把这个方法作为值”)将其传给listFiles方法;请注意,开始用函数代表方法了。稍后会解释这个机制是如何工作的。一个好处是,代码现在读起来更接近问题的陈述了。方法不再是二等值了。与用对象引用传递对象类似(对象引用是用new创建的),在Java 8里写下File::isHidden的时候,就创建了一个方法引用,同样可以传递它。只要方法中有代码(方法中的可执行部分),那么用方法引用就可以传递代码。下图说明了这一概念:
在这里插入图片描述

Lambda——匿名函数
除了允许(命名)函数成为一等值外,Java 8还体现了更广义的将函数作为值的思想,包括Lambda"(或匿名函数)。比如,现在可以写(int x) -> x+1,表示“调用时给定参数x,就返回x+1值的函数”。可能会想这有什么必要呢?因为可以在MyMathsUtils类里面定义一个add1方法,然后写MyMathsUtils::add1嘛!确实是可以,但要是没有方便的方法和类可用,新的Lambda语法更简洁。使用这些概念的程序为函数式编程风格,这句话的意思是“编写把函数作为一等值来传递的程序。”。

传递代码:一个例子

来看一个例子,看看它是如何帮助写程序的。假设有一个Apple类,它有一个getColor方法,还有一个变量inventory保存着一个Apples的列表。想要选出所有的绿苹果,并返回一个列表。通常用筛选(filter)一词来表达这个概念。在Java 8之前,可能会写这样一个方法filterGreenApples:

	public static List<Apple> filterGreenApples(List<Apple> inventory) {List<Apple> result = new ArrayList<>();for (Apple apple: inventory){if("green".equals(apple.getcolor())) {result.add(apple);}}//高亮显示的代码会仅仅选出绿苹果return result;}

但是接下来,有人可能想要选出重的苹果,比如超过150克,于是写了下面这个方法:

public static List<Apple> filterHeavyApples(List<Apple> inventory){List<Apple> result = new ArrayList<>();for (Apple apple: inventory){if(apple.getWeight() > 150){这里高亮显示的代码会result.add(apple);仅仅选出重的苹果}}return result;
}

软件工程中复制粘贴的危险——给一个做了更新和修正,却忘了另一个。这两个方法只有一行不同:if里面的那行条件。如果这两个方法之间的差异仅仅是接受的重量范围不同,那么只要把接受的重量上下限作为参数传递给filter就行了,比如指定(150,1000)来选出重的苹果(超过150克),或者指定(0,80)来选出轻的苹果(低于80克)。

但是,前面提过了,Java8会把条件代码作为参数传递进去,这样可以避免filter方法出现重复的代码。现在可以写:

	public static boolean isGreenApple(Apple apple) {return "green".equals(apple.getcolor());public static boolean isHeavyApple(Apple apple) {return apple.getWeight() > 150;)public interface Predicate<T> { //写出来是为了清晰(平常只要从java.util.function导入就可以了)boolean test(T t);}static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {List<Apple> result = new ArrayList<>();for (Apple apple: inventory){if (p.test(apple)){result.add(apple);}}return result;}

要用它的话,可以写:


filterApples(inventory, Apple::isGreenApple);
或者
filterApples(inventory, Apple::isHeavyApple);
	什么是谓词?前面的代码传递了方法`Apple::isGreenApple`(它接受参数`Apple`并返回一个`boolean`)给`filterApples`,后者则希望接受一个`Predicate<Apple>`参数。谓词(predicate)在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回true或false。在后面会看到,Java8也会允许写Function<Apple, Boolean>——在学校学过函数却没学过谓词的读者对此可能更熟悉,但用Predicate<Apple>是更标准的方式,效率也会更高一点儿,这避免了把boolean封装在Boolean里面。

从传递方法到Lambda

把方法作为值来传递显然很有用,但要是为类似于isHeavyAppleisGreenApple这种可能只用一两次的短方法写一堆定义有点儿烦人。不过Java 8也解决了这个问题,它引入了一套新记法(匿名函数或Lambda):

filterApples(inventory, (Apple a) -> "green".equals(a.getcolor()));

或者

filterApples(inventory, (Apple a) -> a.getWeight() > 150);

甚至

filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getcolor()));

甚至都不需要为只用一次的方法写定义;代码更干净、更清晰,因为用不着去找自己到底传递了什么代码。要是Lambda的长度多于几行(它的行为也不是一目了然)的话,那还是应该用方法引用来指向一个有描述性名称的方法,而不是使用匿名的Lambda。应该以代码的清晰度为准绳。

迄今为止谈到的函数式编程竟然如此强大。本来,Java加上filter和几个相关的东西作为通用库方法就足以让人满意了,比如

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);

这样甚至都不需要写filterApples了,因为比如先前的调用

filterApples(inventory, (Apple a)-> a.getWeight() > 150);

就可以直接调用库方法filter:

filter(inventory, (Apple a) -> a.getWeight() > 150);

但是,为了更好地利用并行,Java的设计师没有这么做。Java 8中有一整套新的类集合API——Stream,它有一套函数式程序员熟悉的、类似于filter的操作,比如mapreduce,还有在Collectionsstreams之间做转换的方法。


http://www.ppmy.cn/news/906715.html

相关文章

Command(命令)

Command&#xff08;命令&#xff09; 行为型 对象 1 Intent_意图2 将“请求”封装成对象&#xff0c;以使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作 Motivation_动机3 在系统中&#xff0c;行为请求者与行为实现者通常是一种紧耦合的关系&…

I - Commando War

先读懂题意&#xff0c;先排序&#xff0c;在相加&#xff0c;贪心算法重在思路。 I - Commando War Time Limit:1000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Submit Status Description G Commando War Input: Standard Input Output: Standard …

commands(commands软件)

QQ任逍遥为什么老“badsequenceofcommands”? bad sequence of commands的意思是&#xff1a;命令的次序错误。 WINDOWS动态链接库中文件丢失会造成软件运行参数错误&#xff0c;就会造成你的这个现象。 所以很可能是你的软件运行时参数有问题&#xff0c;电脑提示bad seque…

Command命令模式

简介&#xff1a; 简单来说&#xff0c;就像我们的复制、删除、插入等等都是命令&#xff0c;我们将命令封装为一个对象&#xff0c;并且支持撤销&#xff0c;将一系列命令串成一条链或者与链表结合使用&#xff0c;可以实现一系列的do和undo 模式类图&#xff1a; command: …

more command

文章目录 1.命令简介2.命令格式3.选项说明4.交互式命令5.环境变量6.常用示例参考文献 1.命令简介 more 是常用的文本文件阅读工具。 more 类似于 cat&#xff0c;不过以一页一页的形式显示&#xff0c;便于逐页阅读。一般文件过大时使用 more 浏览&#xff0c;文件较小时使用…

commandos 系列

盟军敢死队1秘籍2008-03-23 12:42 无敌密技&#xff1a;   游戏进行中打GONZO1982或1982GONZO,然后即可使用如下密技:  SHIFTV--追踪  SHIFTX--瞬间移动  CTRLI--无敌  CTRLSHIFTN--完成任务  CtrlShiftX--敌人全爆  选关密码&#xff1a;  MISSION2: 4JJXB …

K11375 突击战[Commando War,UVa11729]

题目描述 你有N个部下&#xff0c;每个部下需要独立完成一项任务。第i个部下需要你花费Bi分钟交代任务&#xff0c;然后他会立刻独立、无间断地执行Ji分钟后完成任务。你需要选择交代任务的顺序&#xff0c;使得所有任务尽早执行完毕&#xff08;即最后一个执行完的任务应尽早…

du command

文章目录 1.命令简介2.命令格式3.选项说明4.常用示例5.注意事项6.小结参考文献 1.命令简介 du&#xff08;disk usage&#xff09;用于查看指定的目录或文件所占用的磁盘空间。 2.命令格式 du [OPTIONS] [FILES] du [OPTIONS] --files0-fromF3.选项说明 -a, --all递归显示目…