Java 8星期五:Java 8的阴暗面
在Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。
Java 8星期五
每个星期五,我们都会向您展示一些不错的教程风格的Java 8新功能,这些功能利用了lambda表达式,扩展方法和其他好东西。 您可以在GitHub上找到源代码 。
Java 8的阴暗面
到目前为止,我们已经展示了该新主要版本中令人激动的部分 。 但是也有一些警告。 其中很多。 那件事
- ……令人困惑
- … 错了
- …被省略(目前)
- …被省略(很长时间)
Java主要发行版总是有两个方面。 从好的方面来说,我们获得了许多新功能,大多数人会认为这是过期的 。 其他语言,平台早在Java 5之前就有泛型。其他语言,平台早在Java 8之前就有lambda。但是现在,我们终于有了这些功能。 在通常的古怪Java方式中。
Lambda表达式的引入非常优美。 从向后兼容的角度来看,能够将每个匿名SAM实例编写为lambda表达式的想法非常引人注目。 那么,什么是黑暗面到Java 8?
超载变得更糟
重载,泛型和vararg不是朋友。 我们已经在上一篇文章以及这个Stack Overflow问题中 对此进行了解释 。 在您的奇怪应用程序中,这些可能并非每天都有问题,但对于API设计人员和维护人员而言,这是非常重要的问题。
使用lambda表达式,事情变得“更糟”。 因此,您认为可以提供一些便利的API,重载现有的run()
方法(该方法接受Callable
也接受新的Supplier
类型:
static <T> T run(Callable<T> c) throws Exception {return c.call();
}static <T> T run(Supplier<T> s) throws Exception {return s.get();
}
看起来非常有用的Java 7代码现在是Java 8的一大难题。 因为您不能仅使用lambda参数简单地调用这些方法:
public static void main(String[] args)
throws Exception {run(() -> null);// ^^^^^^^^^^ ambiguous method call
}
倒霉。 您将不得不采用以下任一“经典”解决方案:
run((Callable<Object>) (() -> null));run(new Callable<Object>() {@Overridepublic Object call() throws Exception {return null;}});
因此,尽管总有变通办法,但这些变通办法总是“糟透了”。 即使从向后兼容的角度来看事情不会中断,这也真是令人沮丧。
并非所有关键字都支持默认方法
默认方法是一个不错的补充。 有人可能声称Java最终具有特质 。 其他人显然与该术语脱离了关系,例如Brian Goetz:
向Java添加默认方法的主要目标是“接口演变”,而不是“穷人的特征”。
如lambda-dev邮件列表中所示。
事实是,默认方法与Java中的其他任何东西相比都有很多正交和不规则的特征。 这里有一些批评:
他们不能成为最终的
鉴于默认方法也可以用作API中的便捷方法:
public interface NoTrait {// Run the Runnable exactly oncedefault final void run(Runnable r) {// ^^^^^ modifier final not allowedrun(r, 1);}// Run the Runnable "times" timesdefault void run(Runnable r, int times) {for (int i = 0; i < times; i++)r.run();}
}
不幸的是,以上操作是不可能的,因此第一个重载的便捷方法可能会在子类型中被覆盖,即使这对API设计人员而言毫无意义。
无法使其同步
mm! 用语言难以实现吗?
public interface NoTrait {default synchronized void noSynchronized() {// ^^^^^^^^^^^^ modifier synchronized// not allowedSystem.out.println("noSynchronized");}
}
是的, synchronized
很少使用,就像决赛。 但是,当您有了用例时,为什么不仅仅允许它呢? 是什么使接口方法主体如此特别?
默认关键字
这也许是所有功能中最怪异和最不规则的。 default
关键字本身。 让我们比较一下接口和抽象类:
// Interfaces are always abstract
public /* abstract */ interface NoTrait {// Abstract methods have no bodies// The abstract keyword is optional/* abstract */ void run1();// Concrete methods have bodies// The default keyword is mandatorydefault void run2() {}
}// Classes can optionally be abstract
public abstract class NoInterface {// Abstract methods have no bodies// The abstract keyword is mandatoryabstract void run1();// Concrete methods have bodies// The default keyword mustn't be usedvoid run2() {}
}
如果从头开始重新设计该语言,则可能不需要任何abstract
或default
关键字。 两者都是不必要的。 存在或不存在主体的事实足以使编译器评估方法是否抽象。 即,情况应该如何:
public interface NoTrait {void run1();void run2() {}
}public abstract class NoInterface {void run1();void run2() {}
}
上面会更精简和更常规。 遗憾的是,EG从未真正讨论过default
的用途。 好吧,这是经过辩论的,但是EG从来不想接受这种选择。 我已经尝试过运气,下面的响应 :
我不认为#3是一种选择,因为与方法主体的接口一开始是不自然的。 至少指定“默认”关键字为读者提供了某种语言来说明语言允许方法主体的原因。 就个人而言,我希望接口将保持纯合同形式(不执行),但是我不知道有更好的选择来发展接口。
同样,这是EG的明确承诺,即不承诺Java中的“特征”。 默认方法是实现1-2个其他功能的纯粹必要手段。 他们从一开始就没有精心设计。
其他修饰符
幸运的是,在项目后期,使用了static
修饰符使其成为了规范。 因此现在可以在接口中指定静态方法。 但是由于某种原因,这些方法不需要(也不允许!) default
关键字,它必须是EG完全随机决定的,就像您显然无法在接口中定义static final
方法一样。
虽然可见性修饰符已在lambda-dev邮件列表中进行了讨论 ,但超出了此版本的范围。 也许,我们可以在将来的版本中获得它们。
实际上很少执行默认方法
有些方法在接口上会有合理的默认实现–可能会猜测。 直观地,像List
或Set
这样的collections接口会将它们放在其equals()
和hashCode()
方法上,因为这些方法的协定在接口上定义良好。 它还使用listIterator()
在AbstractList
实现,对于大多数定制列表而言,这是合理的默认实现。
如果对这些API进行改造,以使使用Java 8更加容易实现自定义集合,那就太好了。例如,我可以使我的所有业务对象都实现List
,而不会浪费AbstractList
上的单个基类继承。
但是,可能有一个与向后兼容性相关的令人信服的原因阻止了Oracle的Java 8团队实现这些默认方法。 凡是向我们发送此原因的人都将获得免费的jOOQ标签 !
不是在这里发明的-心态
在lambda-dev EG邮件列表中也多次批评了这一点。 而且在撰写本博客系列文章时 ,我只能确认新的功能接口让人很难记住。 由于这些原因,他们感到困惑:
一些原始类型比其他更平等
与所有其他类型相比, int
, long
, double
基本类型是首选的,因为它们在java.util.function包以及整个Streams API中都具有功能接口。 boolean
是二等公民,因为它仍然把它做成包在一个形式BooleanSupplier
或Predicate
,或者更糟: IntPredicate
。
所有其他原始类型在该区域中实际上并不存在。 即没有针对byte
, short
, float
和char
特殊类型。 尽管满足最后期限的争论无疑是有效的,但这种古怪的现状将使新手很难学习这种语言。
类型不仅仅称为函数
坦白说吧。 所有这些类型都只是“功能”。 没有人真正关心Consumer
, Predicate
, UnaryOperator
等之间的隐式差异。
实际上,当您寻找具有非void
返回值和两个参数的类型时,您可能会调用什么呢? Function2
? 好吧,你错了。 它称为BiFunction
。
这是一个决策树,用于了解您要查找的类型如何被调用:
- 您的函数返回
void
吗? 叫做Consumer
- 您的函数返回
boolean
吗? 这叫做Predicate
- 您的函数返回
int
,long
,double
吗? 它叫做XXToIntYY
,XXToLongYY
,XXToDoubleYY
东西 - 您的函数没有参数吗? 叫做
Supplier
- 您的函数是否接受一个
int
,long
,double
参数? 它称为IntXX
,LongXX
,DoubleXX
东西 - 您的函数有两个参数吗? 叫做
BiXX
- 您的函数是否接受两个相同类型的参数? 叫做
BinaryOperator
- 您的函数返回的类型是否与作为单个参数的类型相同? 叫做
UnaryOperator
- 您的函数是否接受两个参数,其中第一个是引用类型,第二个是原始类型? 它称为
ObjXXConsumer
(只有使用该配置的使用者存在) - 否则:称为
Function
好主啊! 最近,我们当然应该去Oracle Education检查一下Oracle Certified Java Programmer课程的价格是否急剧上涨了……幸运的是,有了Lambda表达式,我们几乎不必记住所有这些类型!
有关Java 8的更多信息
Java 5泛型为Java语言带来了许多很棒的新功能。 但是也有很多与类型擦除有关的警告。 Java 8的默认方法,Streams API和lambda表达式将再次为Java语言和平台带来很多很棒的新功能。 但是我们确信, Stack Overflow很快就会因在Java 8丛林中迷路的困惑程序员而引发问题。
学习所有新功能并非易事,但是新功能(和警告)仍然存在。 如果您是Java开发人员,则最好在有机会的时候立即开始练习。 因为我们还有很长的路要走。
翻译自: https://www.javacodegeeks.com/2014/04/java-8-friday-the-dark-side-of-java-8.html
Java 8星期五:Java 8的阴暗面相关推荐
- Java 8中Lambda表达式的阴暗面
这篇文章可能不会使我成为任何新朋友. 哦,好吧,无论如何我从来没有真正在学校受到欢迎. 但是,让我们说清楚. 就语言而言,Java 8的最大特色无疑是Lambda表达式. 几年来,它一直是功能语言(例 ...
- 【Java基础】· Java基本语法:程序流程控制习题总结
写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...
- Java基础知识——Java语言基础
---------------------- ASP.Net+Android+IO开发S. .Net培训.期待与您交流! ---------------------- 2.Java语言基础 Java语 ...
- java 读取txt,java读取大文件
java 读取txt,java读取大文件 package com.bbcmart.util; import java.io.File; import java.io.RandomAccessFile; ...
- JAVA基础(JAVA 执行环境) 第一天
JAVA程序有3中执行环境. (1)能够单独运行的程序,称为Java Application(Java应用程序). (2)在Internet浏览器中运行的程序,称为 Java Applet(JAVA小 ...
- java.utilDate和java.sql.Date
java.utilDate和java.sql.Date由广州疯狂软件教育java培训分享: java程序中的时间类型有java.util.Date和java.sql.Date两种.两者之间的关系和转化 ...
- java 终极超类,Java问答:终极父类(3),java问答
Java问答:终极父类(3),java问答Java问答:终极父类(上) Java问答:终极父类(下) Java问答:终极父类(2)-上篇 Java问答:终极父类(2)-下篇 在之前关于 Object ...
- java.lang.OutOfMemoryError: Java heap space错误及...
为什么80%的码农都做不了架构师?>>> 以下是从网上找到的关于堆空间溢出的错误解决办法: java.lang.OutOfMemoryError: Java heap spac ...
- 浅谈Java SE、Java EE、Java ME三者的区别
1. Java SE(Java Platform,Standard Edition).Java SE 以前称为 J2SE.它允许开发和部署在桌面.服务器.嵌入式环境和实时环境中使用的 Java 应用程 ...
最新文章
- python函数的基础知识_Python入门基础知识点(函数进阶)
- 关于object references an unsaved transient instance - save the transient instance before flushing的解决方法
- POS消费机C#例子代码
- openCV中waitKey函数介绍
- 一台服务器装两个sql server_搭建我的世界基岩版服务器
- 怎样用python自动生成python代码_(Python)自动生成代码(方法一)
- HTML-a标签添加下划线
- 一年多开源没进展,我也放弃了
- 批处理对文件夹、所有子文件夹内的文件重命名、删除
- Ricequant米矿【MACD策略代码解释】量化交易
- 直连线和交叉线的区别
- 读文献——《Deep Residual Learning for Image Recognition》
- 父债子偿有法可依吗?可法院却对这个案子说:不!
- Mysql分页Limt
- 微软2007日语输入法
- 将100以内的所有质数输出
- ORAN专题系列-18:5G O-RAN FrontHaul前传接口互操作性测试规范IOT概述与总体架构
- [杂谈] 机器学习与优化算法的对比
- 超导体磁通穿透,交流损耗ANSYS仿真程序免费下载
- cdoj1339郭大侠与线上游戏
热门文章
- 第13步 用户模块前端(Admin)
- android 处理http状态码,OkHttp(Retrofit)对于http状态码202的处理
- org.springframework.uti包下的StringUtils的使用和org.apache.commons.lang包下StringUtils的使用
- fastdfs 集群 java_FastDFS集群部署(转载 写的比较好)
- 多个会话对表加表级读锁和表级写锁后的操作权限分析(表级读锁+表级写锁)
- 什么是通用字符名称?_通用名称
- 数据库班级字段怎么定义名称_班级名称
- jvm7 jvm8_JVM PermGen –您在哪里?
- jboss架构_检查Red Hat JBoss BRMS部署架构的规则和事件(第一部分)
- activemq网络桥接_ActiveMQ –经纪人网络解释–第4部分