java中已定义类型car_Java 8 习惯用语(8):Java 知道您的类型

215749586_1_20210219095137537.jpeg

Java™8

是第一个支持类型推断的 Java 版本,而且它仅对 lambda 表达式支持此功能。在 lambda

表达式中使用类型推断具有强大的作用,它将帮助您做好准备以应对未来的 Java

版本,在今后的版本中还会将类型推断用于变量等更多可能。这里的诀窍在于恰当地命名参数,相信 Java 编译器会推断出剩余的信息。

大多数时候,编译器完全能够推断类型。在它无法推断出来的时候,就会报错。

了解 lambda 表达式中的类型推断的工作原理,至少查看一个无法推断类型的示例。即使如此,也有解决办法。

显式类型和冗余

假设您询问某个人“您叫什么名字?”,他会回答“我名叫约翰”。这种情况经常发生,但简单地说“约翰”会更高效。您需要的只是一个名称,所以该句子的剩余部分都是多余的。

不幸的是,我们总是在代码中做这类多余的事情。Java 开发人员可以使用 forEach 迭代并输出某个范围内的每个值的双倍值,如下所示:IntStream.rangeClosed(1, 5)

.forEach((int number) -> System.out.println(number * 2));

rangeClosed 方法生成一个从 1 到 5 的 int 值流。lambda 表达式的唯一职责就是接收一个名为 number 的 int 参数,使用PrintStream 的 println 方法输出该值的双倍值。从语法上讲,该 lambda 表达式没有错,但类型细节有些冗余。

Java 8 中的类型推断

当您从某个数字范围中提取一个值时,编译器知道该值的类型为 int。不需要在代码中显式声明该值,尽管这是目前为止的约定。

在 Java 8 中,我们可以丢弃 lambda 表达式中的类型,如下所示:IntStream.rangeClosed(1, 5)

.forEach((number) -> System.out.println(number * 2));

由于

Java 是静态类型语言,它需要在编译时知道所有对象和变量的类型。在 lambda 表达式的参数列表中省略类型并不会让 Java

更接近动态类型语言。但是,添加适当的类型推断功能会让 Java 更接近其他静态类型语言,比如 Scala 或 Haskell。

信任编译器

如果您在 lambda 表达式的一个参数中省略类型,Java 需要通过上下文细节来推断该类型。

返回到上一个示例,当我们在 IntStream 上调用 forEach 时,编译器会查找该方法来确定它采用的参数。IntStream 的 forEach 方法期望使用函数接口 IntConsumer,该接口的抽象方法 accept 采用了一个 int 类型的参数并返回 void。

如果在参数列表中指定了该类型,编译器将会确认该类型符合预期。

如果省略该类型,编译器会推断出预期的类型 —在本例中为 int。

无论是您提供类型还是编译器推断出该类型,Java 都会在编译时知道 lambda 表达式参数的类型。要测试这种情况,可以在 lambda 表达式中引入一个错误,同时省略参数的类型:IntStream.rangeClosed(1, 5)

.forEach((number) -> System.out.println(number.length() * 2));

编译此代码时,Java 编译器会返回以下错误:Sample.java:7: error: int cannot be dereferenced

.forEach((number) -> System.out.println(number.length() * 2));

^1 error

编译器知道名为 number 的参数的类型。它报错是因为它无法使用点运算符解除对某个 int 类型的变量的引用。可以对对象执行此操作,但不能对 int 变量这么做。

类型推断的好处

在 lambda 表达式中省略类型有两个主要好处:键入的内容更少。无需输入类型信息,因为编译器自己能轻松确定该类型。

代码杂质更少 —(number) 比 (int number) 简单得多。

此外,一般来讲,如果我们仅有一个参数,省略类型意味着也可以省略 (),如下所示:IntStream.rangeClosed(1, 5)

.forEach(number -> System.out.println(number * 2));

请注意,您将需要为采用多个参数的 lambda 表达式添加括号。

类型推断和可读性

lambda 表达式中的类型推断违背了 Java 中的常规做法,在常规做法中,会指定每个变量和参数的类型。尽管一些开发人员辩称 Java 指定类型的约定让代码变得更可读、更容易理解,但我认为这种偏好反映出一种习惯而不是必要性。

以一个包含一系列转换的函数管道为例:List result =

cars.stream()

.map((Car c) -> c.getRegistration())

.map((String s) -> DMVRecords.getOwner(s))

.map((Person o) -> o.getName())

.map((String s) -> s.toUpperCase())

.collect(toList());

在这里,我们首先提供了一组 Car 实例和相关的注册信息。我们获取每辆车的车主和车主姓名,并将该姓名转换为大写。最后,将结果放入一个列表中。

这段代码中的每个 lambda 表达式都为其参数指定了一个类型,但我们为参数使用了单字母变量名。这在 Java 中很常见。但这种做法不合适,因为它丢弃了特定于域的上下文。

我们可以做得比这更好。让我们看看使用更强大的参数名重写代码后发生的情况:List result =

cars.stream()

.map((Car car) -> car.getRegistration())

.map((String registration) -> DMVRecords.getOwner(registration))

.map((Person owner) -> owner.getName())

.map((String name) -> name.toUpperCase())

.collect(toList());

这些参数名包含了特定于域的信息。我们没有使用 s 来表示 String,而是指定了特定于域的细节,比如 registration 和name。类似地,我们没有使用 p 或 o,而是使用 owner 表明 Person 不只是一个人,还是这辆车的车主。

这个示例中的每个 lambda 表达式都比它所取代的表达式更好。在读取 lambda 表达式(例如 (Person owner) -> owner.getName())时,我们知道我们获得了车主的姓名,而不只是随便某个人的姓名。

命名参数

Scala 和 TypeScript 等一些语言更加重视参数名而不是类型。在 Scala 中,我们在定义类型之前定义参数,例如通过编写:def getOwner(registration: String)

而不是:def getOwner(String registration)

类型和参数名都很有用,但在 Scala 中,参数名更重要一些。我们用 Java 编写 lambda 表达式时,也可以考虑这一想法。请注意我们在 Java 中的车辆注册示例中丢弃类型细节和括号时发生的情况:List result =

cars.stream()

.map(car -> car.getRegistration())

.map(registration -> DMVRecords.getOwner(registration))

.map(owner -> owner.getName())

.map(name -> name.toUpperCase())

.collect(toList());

因为我们添加了描述性的参数名,所以我们没有丢失太多上下文,而且显式类型(现在是冗余内容)已悄然消失。结果是我们获得了更干净、更朴实的代码。

类型推断的局限性

尽管使用类型推断可以提高效率和可读性,但这种技术并不适用于所有场合。在某些情况下,完全无法使用类型推断。幸运的是,您可以依靠 Java 编译器来获知何时出现这种情况。

我们首先看一个测试编译器并获得成功的示例,然后看一个测试失败的示例。最重要的是,在两种情况下,都能够相信编译器会按期望方式工作。

扩展类型推断

在我们的第一个示例中,假设我们想创建一个 Comparator 来比较 Car 实例。我们首先需要一个 Car 类:class Car {

public String getRegistration() { return null; }}

接下来,我们将创建一个 Comparator,以便基于 Car 实例的注册信息对它们进行比较:public static Comparator createComparator() {

return comparing((Car car) -> car.getRegistration());}

用作 comparing 方法的参数的 lambda 表达式在其参数列表中包含了类型信息。我们知道 Java 编译器非常擅长类型推断,那么让我们看看在省略参数类型的情况下会发生什么,如下所示:public static Comparator createComparator() {

return comparing(car -> car.getRegistration());}

comparing 方法采用了 1 个参数。它期望使用 Function super T, ? extends U> 并返回 Comparator。因为 comparing是 Comparator 上的一个静态方法,所以编译器目前没有关于 T 或 U 可能是什么的线索。

为了解决此问题,编译器稍微扩展了推断范围,将范围扩大到传递给 comparing 方法的参数之外。它观察我们是如何处理调用comparing 的结果的。根据此信息,编译器确定我们仅返回该结果。接下来,它看到由 comparing 返回的 Comparator 又作为 Comparator 由 createComparator 返回 。

注意了!编译器现在已明白我们的意图:它推断应该将 T 绑定到 Car。根据此信息,它知道 lambda 表达式中的 car 参数的类型应该为 Car。

在这个例子中,编译器必须执行一些额外的工作来推断类型,但它成功了。接下来,让我们看看在提高挑战难度,让编译器达到其能力极限时,会发生什么。

推断的局限性

首先,我们在前一个 comparing 调用后面添加了一个新调用。在本例中,我们还为 lambda 表达式的参数重新引入显式类型:public static Comparator createComparator() {

return comparing((Car car) -> car.getRegistration()).reversed();}

借助显式类型,此代码没有编译问题,但现在让我们丢弃类型信息,看看会发生什么:public static Comparator createComparator() {

return comparing(car -> car.getRegistration()).reversed();}

如您下面所见,进展并不顺利。Java 编译器抛出了错误:Sample.java:21: error: cannot find symbol

return comparing(car -> car.getRegistration()).reversed();

^

symbol:   method getRegistration()

location: variable car of type ObjectSample.java:21: error: incompatible types: Comparator cannot be converted to Comparator

return comparing(car -> car.getRegistration()).reversed();

^2 errors

像上一个场景一样,在包含 .reversed() 之前,编译器会询问我们将如何处理调用 comparing(car -> car.getRegistration()) 的结果。在上一个示例中,我们以 Comparable 形式返回结果,所以编译器能推断出 T 的类型为 Car。

但在修改过后的版本中,我们将传递 comparable 的结果作为调用 reversed() 的目标。comparable 返回Comparable,reversed() 没有展示任何有关 T 的可能含义的额外信息。根据此信息,编译器推断 T 的类型肯定是 Object。遗憾的是,此信息对于该代码而言并不足够,因为 Object 缺少我们在 lambda 表达式中调用的 getRegistration() 方法。

类型推断在这一刻失败了。在这种情况下,编译器实际上需要一些信息。类型推断会分析参数、返回元素或赋值元素来确定类型,但在上下文提供的细节不足时,编译器就会达到其能力极限。

能否采用方法引用作为补救措施?

在我们放弃这种特殊情况之前,让我们尝试另一种方法:不使用 lambda 表达式,而是尝试使用方法引用:public static Comparator createComparator() {

return comparing(Car::getRegistration).reversed();}

编译器对此解决方案非常满意。它在方法引用中使用 Car:: 来推断类型。

结束语

Java 8 为 lambda 表达式的参数引入了有限的类型推断能力,在未来的 Java 版本中,会将类型推断扩展到局部变量。现在应该学会省略类型细节并信任编译器,这有助于您轻松步入未来的 Java 环境。

依靠类型推断和适当命名的参数,编写简明、更富于表达且更少杂质的代码。只要您相信编译器能自行推断出类型,就可以使用类型推断。仅在您确定编译器确实需要您的帮助的情况下提供类型细节。原作者:Venkat Subramaniam

原文链接:Java 8 习惯用语

原出处: IBM Developer

215749586_2_20210219095137787.jpeg

来源:https://www.icode9.com/content-1-863301.html

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

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

相关文章

ATM柜员机JAVA课程设计_ATM柜员机学年论文设计(Java课程设计)

内容简介:ATM柜员机学年论文设计(Java课程设计),共23页,4599字,附源程序。一. 程序介绍3二. 开发环境搭建31. MyEclipse 5.5.1 GA安装32. MyEclipse Designer 图形设计插件安装33. MySQL数据库安装4三&…

mysql 结果集什么意思_结果集中的mysql“和”逻辑

假设我有一个类似以下的数据集:table fooid | employeeType | employeeID-------------------------1 | Developer | 12 | Developer | 23 | Developer | 34 | Manager | 15 | Manager | 46 | Manager | 57 | CEO | 18 | CEO | 6我想运行一个查询,该查询将返回所有e…

opencv java 去干扰_java - OpenCV Java修补图像格式要求 - 堆栈内存溢出

一直试图让修复工作在Android上进行,int height (int) viewMat.size().height;int width (int) viewMat.size().width;Mat maskMat new Mat();maskMat.create(viewMat.size(), CvType.CV_8U);maskMat.setTo(bColor);Point r1 new Point(width/2-width/10, heigh…

java中 set集合_第8篇 Java中的集合(Set)

Java 集合的 Set 接口Set类型与List类型的区别Set: 无序、不可重复List: 有序、可重复1、HashSetHashSet的存储结构:HashMap特点:HashSet通过比较存放的哈希码(hashCode)来确定对象存放的位置当两个对象的哈希值相等时&#xff0c…

android mysql实现登录注册_android简单登陆和注册功能实现+SQLite数据库学习

android简单登陆和注册功能实现SQLite数据库学习发布时间&#xff1a;2018-07-04 17:23,浏览次数&#xff1a;1027, 标签&#xff1a;androidSQLite这里我只是建立了一个用简单的存储用户名和密码的表单MyDBHelper.java<>public class MyDBHelper extends SQLiteOpenHelp…

java web 来源页_Java:Java Web--分页效果

先来看一看分页的实现原理万能公式.jpg项目目录.PNG首先,新建Java Web项目一. 梳理业务逻辑重定向到URL(跳转到StudentViewAction页面)//index.jsp页面1.从页面接收可变的值2.接收值有问题时,初始化为13.如果没有问题,把String类型接收值强转成Integer4.实例DAO方法,调用findSt…

java 浏览器 安全_安全策略-IE浏览器防黑十大秘籍

1.管理好Cookie在IE6.0中&#xff0c;打开“工具”→“Internet选项”→“隐私”对话框&#xff0c;这里设定了“阻止所有Cookie”、“高”、“中高”、“中”、“低”、“接受所有Cookie”六个级别&#xff0c;你只要拖动滑块就可以方便地进行设定&#xff0c;而点击下方的“编…

什么是java中的枚举法_enum枚举javajava,enum枚举使用详解+,总结

enum 的全称为 enumeration&#xff0c; 是 JDK 1.5 中引入的新特性&#xff0c;存放在 java.lang 包中。下面是我在使用 enum 过程中的一些经验和总结。原始的接口定义常量语法(定义)创建枚举类型要使用 enum 关键字&#xff0c;隐含了所创建的类型都是 java.lang.Enum 类的子…

java 审计 漏洞函数_Java Web代码审计流程与漏洞函数

常见框架与组合常见框架Struts2SpringMVCSpring Boot框架执行流程View层&#xff1a;视图层Controller层&#xff1a;表现层Service层&#xff1a;业务层Dom层&#xff1a;持久层常见组合SpringStruts2HibernateSpringSpringMVCMybatisSpring BootMybatis代码审计方法根据业务功…

java前期_【JAVA】前期环境配置

一、java的环境配置及在eclipse中如何安装JRE或JDK环境eclipse下载地址&#xff1a;JDK下载地址&#xff1a;1)安装JDK或JRE注&#xff1a;JDK使用与开发者运用&#xff0c;其中包含了开发环境和运行环境。而JRE只包含了java的运行环境。2)配置设置执行路径UNiX&#xff1a;在C…

php截取指定字符串之后,php截取字符串(截取指定字符串之间的字符串)

一、PHP截取两个指定字符后边的字符$a "123abc#456";$b (strpos($a,""));$c (strpos($a,"#"));echo substr($a,$b1,$c-1);二、常用截取字符串技巧。//构造字符串$str "ABCDEFGHIJKLMNOPQRSTUVWXYZ";echo "原字符串&#xff1a;…

php 日志按天截取,Laravel 日志管理:按日期切割日志

日志存储Laravel 默认的错误文件记录在一个文件里&#xff0c;随着时间的推移&#xff0c;此文件将会变得巨大&#xff0c;不方便查阅。我们可以通过修改 config/app.php 配置文件中的 log 选项来配置 Laravel 使用的存储机制。如果你希望每天产生日志都存放在不同的文件中&…

php xcache 方法,php xcache 解密

NO.2 /index.php?actionmd5webcrack 很强大,需要登陆论坛才能解密,点击右上角的“register”进行注册,都是一些简单的单词,应该能看懂吧?...() A.PHP B.JSP D.Ajax 4.配置 ...假设$aarray(‘x’,’y’);,则$aarray_pad...xcache 15 四个模块 Admin Common Member Article 用户…

ecshop category.php?id=4,categoryall.php

//by 琅琊源码 QQ:27392236define(IN_ECS, true);require dirname(__FILE__) . /includes/init.php;if ((DEBUG_MODE & 2) ! 2) {$smarty->caching true;}require ROOT_PATH . /includes/lib_area.php;$area_info get_area_info($province_id);$area_id $area_info[r…

php 获取key的位置,PHP获取当前所在目录位置的方法

本文实例讲述了PHP获取当前所在目录位置的方法。分享给大家供大家参考。具体分析如下&#xff1a;如果要获取脚本文件的目录&#xff0c;要应用函数getcwd()来实现。函数声明如下&#xff1a;string getcwd ( void ) ;成功执行后返回当前目录字符串&#xff0c;失败返回FALSE。…

java8收集器,Java 8中的收集器collectionAndThen()方法

collectingAndThen()Java Collectors类中的方法使Collector适应于执行其他完成转换。它返回执行下游收集器动作的收集器&#xff0c;然后执行附加的结束步骤。语法如下。static Collector collectingAndThen(Collector downstream, Functionfinisher)在这里&#xff0c;参数T-…

php 精度运算,PHP BC 库(任意精度数字运算) | 网游世界

留意&#xff1a;备选参数$scale以设置运算精度(保留小数位)。bcscale(设置运算精度)bool bcscale ( int $scale )说明&#xff1a;设置运算精度(保留小数位)&#xff0c;成功返回TRUE否则为FALSE。bcadd(加法运算)string bcadd ( string $left_operand , string $right_operan…

php 不允许外部访问,[日常] 解决mysql不允许外部访问

1.在端口已经开放的情况下,ubuntu mysql 3306允许远程访问vim /etc/mysql/mysql.conf.d/mysqld.cnf注释#bind-address 127.0.0.12.给用户授权允许远程访问:grant all privileges on *.* to root"%" identified by "pwd" with grant option;flush privileg…

elementary安装Java,elementary os怎么样安装java

elementary os的安装方法elementary os 的安装镜像文件准备好以后&#xff0c;这里以虚拟机上安装为例&#xff0c;配置好以后启动虚拟机进入安装界面。在初始化安装界面中先选择“中文简体”&#xff0c;再点击“安装elementary os”按钮随后系统会显示硬件安装需求界面&#…

java jni框架,Java JNI 简明教程(一)——传智播客JNI笔记(王泽佑)

package cn.itcast;public calss TestNative {public native void sayHello();public static void main(String[] arg){}}2. 用javah.exe生成包含native方法的C/C头文件javah -jni(默认)javah cn.itcast.TestNative //由类名执行生成C/C头文件生成的头文件内容&#xff1a;JNIE…