Java 8发布了! — Lambdas教程

为了庆祝几分钟前发布的Java 8的发布,我正在发布Java 8 Lambdas教程的草稿版本。 这是学习Streams API的一种很好的可视化方法,从第一天开始,它将帮助您开始在自己的应用程序中利用lambda。本文计划在下一期Java Magazine发行中发表,因此,请期盼最终的。版本,如果它们在出版截止日期之前,我会尽力纳入评论和反馈。

图片1

玛丽有个小拉姆达

自从Java 5中的泛型发布以来,Java Lambda是进入Java语言最有影响力的功能。它从根本上改变了编程模型,允许开发一种功能样式,并支持有效的代码并行化以利用多核系统。 尽管作为Java开发人员,您首先会注意到使用Java 8中启用了lambda的新API所带来的生产率提高。

在本文中,我们将通过使用JavaFX编写的复古游戏向您介绍新的Streams API,以使用集合和数据。 该游戏既是一个从头开始编写的简单Java 8应用程序,用于展示lambda的最佳实践,又是使用Streams API进行编程的直观指南。 但是,我们将首先通过介绍lambdas语言更改来奠定基础。

Lambdas简介

要使用lambda,必须使用最新的Java SDK(8或更高版本),并在编译时将语言级别设置为Java 8。 您可以从以下位置下载最新的Java SDK版本:

  • http://www.oracle.com/technetwork/java/javase/downloads/index.html

使用支持新语法的IDE时,开发lambda变得容易得多。 大多数Java IDE已更新为具有lambdas支持,并将帮助您进行lambdas的实时错误报告和代码完成。 NetBeans和IntelliJ值得一提,因为它们在Java 8发行时具有开箱即用的最佳lambda支持,并且两者在我们在此演示的示例中均能很好地工作。

为了演示新的lambdas功能是如何工作的,下面是一小段代码,它循环访问形状列表并将蓝色的形状更改为红色:

for (Shape s : shapes) {if (s.getColor() == BLUE)s.setColor(RED);
}

在Java 8中,您可以通过使用如下的forEach和lambda表达式来重写相同的代码:

shapes.forEach(s -> {if (s.getColor() == BLUE)s.setColor(RED);
});

lambda表单在Collection接口上使用了一个称为forEach的新方法,该方法采用lambda表达式并对所有包含的元素求值。 为了简化lambda表达式的使用,整个Java核心类都进行了类似的API增强。

您可能遇到的一个相关问题是Java团队如何在不破坏向后兼容性的情况下向接口添加新方法。 例如,如果您具有实现Collection接口的代码,但未定义forEach方法,那么升级到Java 8不会破坏您的实现吗? 幸运的是,另一个称为扩展方法的功能解决了Java 8中的此问题。下面的代码清单显示了Collection接口上的forEach实现:

interface Collection<T> {default void forEach(Block<T> action) {Objects.requireNonNull(action);for (T t : this)action.apply(t);}// Rest of Collection methods…
}

注意新的默认关键字,它指示该方法后将带有默认实现。 子类可以自由创建自己的方法实现,但是如果未定义子类,则它们将获得与接口中定义的相同的标准行为。 这允许将新方法添加到核心Java类以及您自己的库和项目中的现有接口。

实际的lambda语法非常简单……以完整的形式在左侧提供类型和参数,在中间添加一个破折号,大于号[->],并在其后加上花括号的方法主体:

(int a, int b) -> { return a + b; }

在函数返回值的情况下,可以通过删除花括号,return关键字和分号来简化此操作:

(a, b) -> a + b

此外,在只有一个参数的情况下,您可以省略括号:

a -> a * a

最后,如果没有参数,则只需将括号留空,这对于替换Runnable实现或其他无参数方法很常见:

() -> { System.out.println("done"); }

除了基本语法外,还有一种特殊的快捷方式语法称为“方法引用”,它使您可以快速创建将单个方法引用为实现的lambda表达式。 下表总结了不同类型的方法引用以及等效的长格式lambda语法。

方法参考 等效λ
对象:: toString obj-> Objects.toString(obj) 静态方法参考
对象:: toString obj-> obj.toString() 会员方法参考
obj :: toString ()-> obj.toString() 对象方法参考
对象::新 ()->新的Object() 构造方法参考

使用新的lambdas方法时最重要的最后一个概念是创建允许您接受lambda表达式的接口。 为此,具有一个显式声明的抽象方法的任何接口都可以用于接受lambda表达式,因此被称为功能接口。

为了方便起见,他们引入了新的FunctionalInterface批注,可以选择使用该批注来标记接口,以便在检查以确保您的接口满足单个显式声明的抽象方法要求时从编译器获取帮助:

@FunctionalInterface
interface Sum {int add(int a, int b);
}

这是推荐的最佳实践,因为它会捕获功能接口定义中的一些极端情况,例如包含默认方法,这些默认方法使您可以在功能接口上定义多个方法,因为它们不是抽象的,并且不计入单一抽象方法要求。

现在您已经对lambda语法有了基本的了解,是时候探索流API并在一个可视示例的上下文中展示lambda的功能了。

Lambdas复古游戏

玛丽有点lambda

谁的羊毛洁白如雪

玛丽去过的任何地方

Lambda一定会去!

图片2 如今,视频游戏都是关于高分辨率3D图形,电影品质的剪切场景以及从新手到和平主义者的难度级别。 但是,在游戏的美好时光中,我们只有精灵……可爱,像素化的小人物跳舞和RPG穿越精心设计的疯狂难度关卡。

基于Sprite的图形也很容易编程,使我们能够用不到400行代码构建完整的动画系统。 完整的应用程序代码在GitHub的以下位置:

  • https://github.com/steveonjava/ MaryHadALittleLambda

对于游戏中使用的所有图形,图像以标准的3×4平铺格式进行布局,如Mary的相邻Sprite表中所示。 (当然)使用Lambda完成了动画精灵的代码,只需在平铺的图像周围移动视口即可产生3帧的行走动画[水平]并更改角色朝向的方向[垂直]。

ChangeListener<Object> updateImage =(ov, o, o2) -> imageView.setViewport(new Rectangle2D(frame.get() * spriteWidth,direction.get().getOffset() * spriteHeight,spriteWidth, spriteHeight));
direction.addListener(updateImage);
frame.addListener(updateImage);

为背景添加静态图像,并添加一些关键事件侦听器以在输入时移动角色,您便拥有了经典RPG游戏的基础知识!

图片3

产生流

有几种创建新Java 8 Stream的方法。 最简单的方法是从您选择的集合开始,然后简单地调用stream()或parallelStream()方法来获取Stream对象,如以下代码片段所示:

anyCollection.stream();

您还可以使用Stream类上的静态帮助器方法从一组已知的对象返回流。 例如,要获取包含一组字符串的流,可以使用以下代码:

Stream.of("bananas", "oranges", "apples");

同样,您可以使用Stream数字子类(例如IntStream)取回生成的一系列数字:

IntStream.range(0, 50)

但是,生成新系列最有趣的方法是在Stream类上使用generate和iterate方法。 这些使您可以使用lambda创建新的对象流,该lambda被调用以返回新对象。 迭代方法特别有趣,因为它将先前创建的对象传递给lambda。 这使您可以为每个调用返回一个不同的对象,例如迭代地返回彩虹中的所有颜色:

Stream.iterate(Color.RED,c -> Color.hsb(c.getHue() + .1, c.getSaturation(),c.getBrightness()));

为了演示它在视觉上是如何工作的,我们将在踩到绵羊的应用程序中添加一个新元素。

新的Barn类的代码如下:

public static class Barn extends MapObject {static final Image BARN = loadImage("images/barn.png");public Barn(Main.Location loc) {super(BARN, loc);}@Overridepublic void visit(Shepherd s) {SpriteView tail = s.getAnimals().isEmpty() ?s : s.getAnimals().get(s.getAnimals().size() - 1);Stream.iterate(tail, SpriteView.Lamb::new).skip(1).limit(7).forEach(s.getAnimals()::add);}
}

这段代码指定了用于基于Sprite的图形的图像,该图像被传递给超级构造函数,并实现了一个visit方法,该方法具有当Mary踏上谷仓时将被执行的逻辑。

visit方法中的第一条语句只是从Mary后面的动物列表中获取最后一个元素,如果还没有动物,则返回她。 然后将其用作iterate方法的种子,该方法将被传递给Lamb构造函数以进行lambda的首次调用。 然后,由此生成的羔羊将被传递给Lamb构造函数以进行第二次调用,并且此过程将连续重复。

结果流包括种子,因此我们可以使用skip函数从流中删除该种子,并且从理论上讲它是无限的。 由于流是惰性的,因此在添加终端操作之前,我们不必担心会创建对象,但是固定流长度的一种简单方法是使用limit函数,我们将参数7设置为跟随玛丽生出七只羊。 最后一步是添加将使用该流的终端操作。 在这种情况下,我们将使用forEach函数,并将lambda表达式设置为对动物列表中add方法的方法引用。 执行此lambda的结果是接连跟随Mary的七个羔羊:

图片4

我们要添加到游戏中的下一个元素是彩虹,它将演示Streams API中的过滤。 过滤器函数的工作方式是采用谓词lambda,该谓词对流中的每个元素求值为true或false。 结果流包含谓词lambda评估为true的所有元素。

对于彩虹的逻辑,我们将执行一个过滤器,该过滤器返回流中每 4 动物,并应用JavaFX ColorAdjust函数来改变色相以匹配传入的颜色。 对于白色,我们使用null(无颜色偏移)。 以下代码是Rainbow MapObject的visit方法的实现:

s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 1).forEach(a -> a.setColor(null));
s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 2).forEach(a -> a.setColor(Color.YELLOW));
s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 3).forEach(a -> a.setColor(Color.CYAN));
s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 0).forEach(a -> a.setColor(Color.GREEN));

当玛丽踩到彩虹时,所有的羔羊都会根据您指定的颜色值着色:

图片5

“ Lamb” da问题1:如果您在参观彩虹后踏上谷仓会怎样?

使用过滤的另一种方法是利用添加到Collection API的新方法来接受谓词lambda。 其中包括removeIf,它过滤掉与给定谓词不匹配的所有元素,并进行过滤(位于ObservableList上)并返回只包含与谓词匹配的项的FilteredList。

我们将使用它们来实现Church对象,该对象将过滤“纯”动物。 教会职员会煮熟任何白色的动物来喂养有需要的人。 这包括增加标牌上“已送餐”的计数器,并从列表中删除“纯”动物。 教会拜访方法的代码如下所示。

Predicate<SpriteView> pure =a -> a.getColor() == null;mealsServed.set(mealsServed.get() +s.getAnimals().filtered(pure).size()
);s.getAnimals().removeIf(pure);

在以下屏幕截图中,您可以看到连续踩彩虹和教堂的结果。

图片6

问题2:是否可以在所有动物上色后使用教堂清理它们?

Streams API中最强大的操作可能是地图功能。 这使您可以将流中的所有元素从一种对象类型转换为另一种对象,并在此过程中执行强大的转换。 我们将用它来实现鸡舍,所有跟随玛丽的动物都将被转换成卵。

对于鸡舍,我有两种访问方法的实现。 第一个使用带有lambda表达式的单个map操作用鸡蛋替换流元素,如下所示:

// single map:
s.getAnimals().setAll(s.getAnimals().stream().map(sv -> new Eggs(sv.getFollowing())
).collect(Collectors.toList()));

第二种实现使用方法引用和一组映射操作链来首先将流转换为动物所跟随的流,然后调用构造函数方法引用来创建卵,并将以下信息传递给构造函数参数:

// or a double map:
s.getAnimals().setAll(s.getAnimals().stream().parallel().map(SpriteView::getFollowing).map(Eggs::new).collect(Collectors.toList())
);

这两个代码片段的行为和执行相似,因为流API被设计为惰性的,并且仅在调用终端操作(例如collect)时评估流。 因此,它主要是您更喜欢使用的样式问题。 使用新的鸡舍MapObject运行该程序,可以让您从羔羊生成鸡蛋,如下图所示:

图片7

问题3:如果您将彩色的羔羊送到鸡舍,鸡蛋是什么颜色?

请注意,每个鸡蛋精灵包含三个小弹跳鸡蛋。 如果我们能把这些家伙孵化成鸡,那不是很好吗?

要孵化这些鸡蛋,我们将为巢添加一个新的MapObject,其中使用以下孵化方法将这些鸡蛋孵化成三只鸡的组:

public static Stream<SpriteView> hatch(SpriteView sv) {if (!(sv instanceof Eggs)) {return Stream.of(sv);}return Stream.iterate(sv, Chicken::new).skip(1).limit(3);
}

请注意,此方法返回一个对象流,这意味着,如果我们使用常规映射操作,则将返回一个流流。 为了将Stream扁平化为单个鸡列表,我们可以改用flatMap,它既可以使用lambda函数映射流,也可以将嵌套的Stream折叠为单个对象列表。 使用flatMap的嵌套访问功能的实现如下所示:

s.getAnimals().setAll(s.getAnimals().stream().parallel().flatMap(SpriteView.Eggs::hatch).collect(Collectors.toList())
);

现在,将鸡蛋放到巢中后,您将得到爆炸的鸡,如以下屏幕截图所示:

图片8

问题4:在游戏内存不足之前,您可以添加大约几只动物?

我们将添加的最后一个元素是一只狐狸,以演示如何减少流。 为此,我们将首先根据动物的规模将流映射到整数列表,然后使用sum方法引用将其减少为单个值。 reduce函数需要一个种子值(我们将使用0作为其值),以及一个可以将两个元素简化为单个结果的函数。 该lambda将递归应用于流中的所有元素,直到得到单个值,该值将是所有动物比例的总和。

Double mealSize = shepherd.getAnimals().stream().map(SpriteView::getScaleX).reduce(0.0, Double::sum);setScaleX(getScaleX() + mealSize * .2);
setScaleY(getScaleY() + mealSize * .2);
shepherd.getAnimals().clear();

然后,我们取总和(存储到名为mealSize的变量中),并使用它来按比例拉伸狐狸。 您可以在下图中看到为狐狸吃一顿美味的食物的结果:

图片9

问题5:您如何更改Fox的代码以使其在进食时变胖?

在本文中,我们介绍了基本的lambda语法,包括方法引用,扩展方法和功能接口。 然后,我们在Streams API中进行了详细介绍,展示了一些常见的操作,例如迭代,过滤,映射,flatMap和reduce。 如您所见,Java 8 lambda极大地改变了编程模型,使您可以编写更简单,更精美的代码,并为诸如Streams之类的新功能强大的API开辟了可能性。 现在是时候在您自己的开发中开始利用这些功能了。

翻译自: https://www.javacodegeeks.com/2014/03/java-8-released-lambdas-tutorial.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

angular学习的一些小笔记(中)之表单验证

表单验证 我去&#xff0c;我感觉我这个人其实还是一个很傻逼的一个人&#xff0c;老是因为拼错了一个单词或者怎么样就浪费我很长时间&#xff0c;这样真的不行不行&#xff0c;要正确对待这个问题&#xff0c;好了&#xff0c;说正题吧&#xff0c;angular也有表单验证minle…

html 表单优化,用CSS3优化HTML5表单的步奏

今天给大家带来用CSS3来优化HTML5表单的方法&#xff0c;首先我们创建一个表单&#xff0c;格式如下。#redemption {width: 100%;font-family: ColaborateThinRegular;font-weight: 400;}#redemption hgroup {argin-bottom: 20px;}#redemption div {width: 100%;margin-bottom:…

redis 学习(18)-- AOF

redis -- AOF 什么是 AOF 通过日志方式将redis中的写命令进行日志记录&#xff0c;保存在硬盘文件中。 日志记录的实质是将写命令写在硬盘的缓冲区中&#xff0c;再根据相关策略把数据刷新到磁盘中。 当redis服务器启动时候&#xff0c;执行硬盘中的日志文件以恢复redis中的数据…

重启IIS和SqlServer的命令行

在WEB开发中经常需要重启IIS,每次打开IIS来操作很麻烦&#xff0c;所以我干脆就在桌面新增了一个CMD文件&#xff0c;内容如下&#xff1a;&#xff08;SQL Server占的内存不少&#xff09;net stop iisadmin /ynet start w3svc net stop mssqlservernet start mssqlserver每次…

精简SWT FormLayout的用法

出于对效率的追求&#xff0c;我最近重新考虑了SWT FormLayout的可用性。 尽管就灵活性而言&#xff0c;它是我最喜欢的核心布局之一&#xff0c;但我不得不认识到&#xff0c;大多数同事都不情愿地使用它。 考虑到面部反应&#xff0c;建议将其建议用于适当的任务有时实际上会…

vue 启动时卡死_十分钟浅入Vue 原理

vue原理引用众所周知vue是一个MVVM 渐进式框架&#xff0c;MVVM是vue的设计模式&#xff0c;在vue框架中数据会自动驱动视图。1、MVVM设计模式 ​ 解释View是视图&#xff0c;就是DOM&#xff1b;对应视图也就是HTML部分--代表UI组件&#xff0c;它负责将数据模型转化成UI展现出…

可以使用计算机解决的问题是什么,1.1 使用计算机解决问题的一般过程教案1

算法及其实现【学习目标】1、了解算法的含义2、了解算法的表示方法3、会用流程图表示算法4、能正确理解流程图中算法的意义【重难点】正确理解流程图中算法的意义【问题引导】问&#xff1a;在考试练习中&#xff0c;同学们一定遇到这种题&#xff0c;你们怎么来做&#xff1f;…

centos7.3 安装 mysql-5.7.13

系统环境: [rootlocalhost ~]# cat /etc/RedHat-release CentOS release 6.7 (Final)[rootlocalhost tools]# uname -aLinux localhost 2.6.32-573.22.1.el6.x86_64 #1 SMP Wed Mar 23 03:35:39 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux[rootlocalhost tools]# 软件准备:[root…

GARFIELD@12-20-2004

克已复礼为仁转载于:https://www.cnblogs.com/rexhost/archive/2004/12/20/79502.html

Java 8的功能基础

Java 8彻底改变了Java。 它很可能是过去10年中最重要的Java版本。 有很多新功能&#xff0c;包括默认方法&#xff0c;方法和构造函数引用以及lambda&#xff0c; 仅举几例 。 更有趣的功能之一是新的java.util.stream API&#xff0c;正如Javadoc所述&#xff0c;该API支持 …

面向对象克隆对象

克隆对象这种写法不是对象克隆&#xff0c;就是把obj的内存地址赋值给obj2. 通过for in克隆 不管是公有的还是私有的都克隆成私有的 Js提供了一个一个克隆的方法 Object.create() Var obj2Object.create(obj) 将obj的所有属性克隆到obj2的原型上 转载于:https://www.cnblogs.…

JSON.stringify()和JOSN.parse()

JSON.stringify()跟JSON.parse() 终于把这两个方法搞清楚了&#xff01;&#xff01;&#xff01; JSON.tringify()&#xff1a;把一个json数据转化成JSON string JSON.stringify({uno:1,dos:2},null,\t)"{"uno": 1,"dos": 2}"JSON.stringfy({u…

查表法实现反正切_关于python实现CRC32的应用和总结

关于python实现CRC32的应用和总结目前使用的Crc计算包含Crc32和Crc32mpeg2两种计算方式。循环冗余检验 CRC 差错检测技术能够证明数据是完整的&#xff0c;是无差错的&#xff08;只是非常近似的认为是无差错的&#xff09;。保证数据可靠性传输的方法包含如下&#xff1a;检验…

bootstrap html5 表单验证,基于Bootstrap+jQuery.validate实现表单验证

这大概是一种惯例&#xff0c;学习前台后台最开始接触的业务都是用户注册和登录。现在社会坚持以人为本的理念&#xff0c;在网站开发过程同样如此。User是我们面对较多的对象&#xff0c;也是较核心的对象。最开始的用户注册和登陆这块&#xff0c;也就尤为重要。用户注册和登…

小程序支付及H5支付前端代码小结

小程序支付和H5支付前端都不需要引入其他的js , 只需要后台将相关的参数 &#xff08; timeStamp: , nonceStr: , package: , signType: MD5, paySign: , &#xff09; 返回来就可以发起微信支付。 小程序支付&#xff1a; wx.requestPayment({timeStamp: ,nonceStr: ,package:…

一道有意思的面试算法题

新年第一篇文章&#xff0c;先祝大家新年快乐&#xff01;&#xff01;那么接下来进入正文。 前言 前阵子突发奇想&#xff0c;突然开始刷leetcode。其中刷到了一道有意思的题目&#xff0c;发现这道题是当时秋招的时候&#xff0c;腾讯面试官曾经问过我的题目。于是分享给大家…

JavaFX技巧1:可调整大小的Canvas

在使用FlexGanttFX时&#xff0c;我不得不处理很多JavaFX Canvas节点。 我正在使用它在时间轴上呈现活动。 甘特图中的每一行都是一个Canvas节点。 用户可以选择单独调整每行的大小。 因此&#xff0c;我不得不找出调整画布大小的最佳方法&#xff0c;这种现成的方法无法调整大…

Javolution 2.2.5 - Java Struct/Union Simplified

Multi-dimensional arrays of struct/union or of primitive types are also supported. Struct API: http://javolution.org/api/javolution/io/Struct.html转载于:https://www.cnblogs.com/perlye/archive/2005/02/04/102192.html

function

function foo(){function bar(){return 3;}return bar();function bar(){return 8;}}1.这个函数返回的是8 function foo1(){var bar1function(){return 3;};return bar1();var bar1function(){return 8;}}2.这个函数返回是3 alert(foo2());function foo2(){var bar2function(){…

12日疯人认证百度云_12月15日上海实习汇总(百度、美图、哔哩哔哩等)

上海实习实习汇总 | 实习内推 | 寒假实习博世 采购助理Job Title: 采购部实习生Job Description:1. Create Purchase Decision (PD), initiate internal approval workflow.2. Maintain basic data in SAP, SupplyOn, SRM, internal system Pilum .etc.3. Issue purchase order…