您会后悔对Lambdas应用重载!
编写好的API很难。 非常辛苦。 如果您想让用户喜欢您的API,则必须考虑很多事情。 您必须在以下两者之间找到适当的平衡:
- 有用性
- 易用性
- 向后兼容
- 前向兼容性
之前,在我们的文章: 如何设计良好的常规API中,我们已经就此主题进行过博客讨论。 今天,我们将研究如何…
Java 8更改规则
是!
重载是一个很好的工具,可以在两个方面提供便利:
- 通过提供参数类型替代
- 通过提供参数默认值
来自JDK的上述示例包括:
public class Arrays {// Argument type alternativespublic static void sort(int[] a) { ... }public static void sort(long[] a) { ... }// Argument default valuespublic static IntStream stream(int[] array) { ... }public static IntStream stream(int[] array, int startInclusive, int endExclusive) { ... }
}
jOOQ API显然充满了这种便利。 由于jOOQ是SQL的DSL ,我们甚至可能会滥用一点:
public interface DSLContext {<T1> SelectSelectStep<Record1<T1>> select(SelectField<T1> field1);<T1, T2> SelectSelectStep<Record2<T1, T2>> select(SelectField<T1> field1, SelectField<T2> field2);<T1, T2, T3> SelectSelectStep<Record3<T1, T2, T3>> sselect(SelectField<T1> field1, SelectField<T2> field2, SelectField<T3> field3);<T1, T2, T3, T4> SelectSelectStep<Record4<T1, T2, T3, T4>> select(SelectField<T1> field1, SelectField<T2> field2, SelectField<T3> field3, SelectField<T4> field4);// and so on...
}
诸如Ceylon之类的语言通过声称以上内容是在Java中使用重载的唯一合理原因,将便利性这一概念进一步提高了。 因此,锡兰(Ceylon)的创建者已完全消除了其语言中的重载,将以上内容替换为联合类型和参数的实际默认值。 例如
// Union types
void sort(int[]|long[] a) { ... }// Default argument values
IntStream stream(int[] array,int startInclusive = 0,int endInclusive = array.length) { ... }
阅读“我希望我们在Java中拥有的十大锡兰语言功能” ,以获取有关锡兰的更多信息。
不幸的是,在Java中,我们不能使用联合类型或参数默认值。 因此,我们必须使用重载为API使用者提供便捷的方法。
但是,如果您的方法参数是一个函数接口 ,则在方法重载方面,Java 7和Java 8之间的情况发生了巨大变化。 JavaFX在此提供了一个示例。
JavaFX的“不友好”的ObservableList
JavaFX通过使它们“可观察”来增强JDK集合类型。 不要与Observable
混淆, Observable
是JDK 1.0和Swing之前的恐龙类型。
JavaFX自己的Observable
本质上是这样的:
public interface Observable {void addListener(InvalidationListener listener);void removeListener(InvalidationListener listener);
}
幸运的是,这个InvalidationListener
是一个功能接口:
@FunctionalInterface
public interface InvalidationListener {void invalidated(Observable observable);
}
这很棒,因为我们可以做以下事情:
Observable awesome = FXCollections.observableArrayList();
awesome.addListener(fantastic -> splendid.cheer());
(请注意,我是如何用更愉快的术语替换foo / bar / baz的。我们都应该这样做。Foo和bar是如此1970 )
不幸的是,当我们做我们可能要做的事情时,事情变得更加繁琐。 也就是说,与其声明一个Observable
, Observable
是一个更加有用的ObservableList
:
ObservableList<String> awesome = FXCollections.observableArrayList();
awesome.addListener(fantastic -> splendid.cheer());
但是现在,我们在第二行收到编译错误:
awesome.addListener(fantastic -> splendid.cheer());
// ^^^^^^^^^^^
// The method addListener(ListChangeListener<? super String>)
// is ambiguous for the type ObservableList<String>
因为,本质上...
public interface ObservableList<E>
extends List<E>, Observable {void addListener(ListChangeListener<? super E> listener);
}
和…
@FunctionalInterface
public interface ListChangeListener<E> {void onChanged(Change<? extends E> c);
}
再一次,在Java 8之前,这两种侦听器类型是完全可区分的,并且仍然是。 您可以通过传递命名类型来轻松调用它们。 如果我们编写以下代码,我们的原始代码仍然可以使用:
ObservableList<String> awesome = FXCollections.observableArrayList();
InvalidationListener hearYe = fantastic -> splendid.cheer();
awesome.addListener(hearYe);
要么…
ObservableList<String> awesome = FXCollections.observableArrayList();
awesome.addListener((InvalidationListener) fantastic -> splendid.cheer());
甚至…
ObservableList<String> awesome = FXCollections.observableArrayList();
awesome.addListener((Observable fantastic) -> splendid.cheer());
所有这些措施将消除歧义。 但坦率地说,如果您必须显式键入lambda或参数类型,则lambda的性能只有后者的一半。 我们拥有现代化的IDE,它们可以执行自动补全并帮助推断类型,就像编译器本身一样。
想象一下,如果我们真的想调用另一个addListener()
方法,它需要一个ListChangeListener。 我们必须写任何
ObservableList<String> awesome = FXCollections.observableArrayList();// Agh. Remember that we have to repeat "String" here
ListChangeListener<String> hearYe = fantastic -> splendid.cheer();
awesome.addListener(hearYe);
要么…
ObservableList<String> awesome = FXCollections.observableArrayList();// Agh. Remember that we have to repeat "String" here
awesome.addListener((ListChangeListener<String>) fantastic -> splendid.cheer());
甚至…
ObservableList<String> awesome = FXCollections.observableArrayList();// WTF... "extends" String?? But that's what this thing needs...
awesome.addListener((Change<? extends String> fantastic) -> splendid.cheer());
必须警惕。
API设计很难。 以前很难,现在变得越来越难。 在Java 8中,如果您的API方法的任何参数是功能接口,请三思而后行重载该API方法。 一旦您确定要继续进行重载,请再次思考,这是否真的是一个好主意。
不服气吗? 仔细看一下JDK。 例如java.util.stream.Stream
类型。 您看到多少个具有相同数量的功能接口参数的重载方法,而这些接口又使用了相同数量的方法参数(就像我们前面的addListener()
示例中一样)?
零。
在重载参数编号不同的地方有重载。 例如:
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);<R, A> R collect(Collector<? super T, A, R> collector);
调用collect()
时,您永远不会有任何歧义。
但是,如果参数编号没有不同,并且参数本身的方法参数编号也没有变化,则方法名称也不同。 例如:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
现在,这在呼叫站点上非常令人讨厌,因为您必须事先考虑必须根据各种相关类型使用哪种方法。
但这确实是解决这一难题的唯一方法。 因此,请记住: 您会为Lambdas应用重载感到遗憾!
翻译自: https://www.javacodegeeks.com/2015/02/you-will-regret-applying-overloading-with-lambdas.html
您会后悔对Lambdas应用重载!相关推荐
- c++ lambda 重载_您会后悔对Lambdas应用重载!
c++ lambda 重载 编写好的API很难. 非常辛苦. 如果您希望用户喜欢您的API,则必须考虑很多事情. 您必须在以下两者之间找到适当的平衡: 用处 易用性 向后兼容 前向兼容性 之前,在我们 ...
- java方法重载和重载方法_Java 8的方法参考进一步限制了重载
java方法重载和重载方法 方法重载一直是一个充满喜忧参半的话题. 我们已经在博客上介绍了它,并介绍了几次警告: 您会后悔对Lambdas应用重载! 保持干燥:方法重载 为什么每个人都讨厌操作员超载 ...
- Java 8的方法参考进一步限制了重载
方法重载一直是一个充满喜忧参半的话题. 我们已经在博客上介绍了它,并介绍了几次警告: 您会后悔对Lambdas应用重载! 保持干燥:方法重载 为什么每个人都讨厌操作员超载 API设计师,请小心 重载有 ...
- 【C++】重载运算符(二)
1.4 下标运算符p501 下标运算符必须是成员函数,表示容器的类通常可以通过容器中的位置访问元素,定义下标运算符operator[] 一个包含下标运算符的类,通常,定义2个版本:一个返回普通引用,另 ...
- python 重载的实现(single-dispatch generic function)
DAY 11. python 重载 函数重载是指允许定义参数数量或类型不同的同名函数,程序在运行时会根据所传递的参数类型选择应该调用的函数 ,但在默认情况下,python是不支持函数重载的,定义同名函 ...
- C++ 重载运算符 operator
operator 是什么 operator 是C++的一个关键字,它和运算符(+,-,*,/,=,等等)一起使用,表示一个运算符重载函数 operator 没有返回语句 operator 的作用 : ...
- C++ 笔记(13)— 函数(函数声明、函数定义、函数调用[传值、指针、引用]、函数参数默认值、函数重载)
每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数. 1. 函数声明 函数声明告诉编译器函数的名称.返回类型和参数.函数声明包括以下几个部分: ret ...
- Java知多少(29)覆盖和重载
在类继承中,子类可以修改从父类继承来的方法,也就是说子类能创建一个与父类方法有不同功能的方法,但具有相同的名称.返回值类型.参数列表. 如果在新类中定义一个方法,其名称.返回值类型和参数列表正好与父类 ...
- Const 重载解析
1. Const重载应用场景 首先,对于函数值传递的情况,因为参数传递是通过复制实参创建一个临时变量传递进函数的,函数内只能改变临时变量,但无法改变实参.则这个时候无论加不加const对实参不会产生任 ...
最新文章
- Moodle网络课程上如何添加视频文件
- 看别人的C/C++代码时发现自己所不知道的语法~
- 一篇文章了解蛋白质组学研究
- 实例源码_SpringBoot数据库源码解析Template实例化操作
- c#获取机器唯一识别码
- Springboot基于thymeleaf的一个简单的学生管理系统
- Django下载服务器文件到本地
- html背景颜色代码格式,html常用背景颜色代码.docx
- CSS 3之 文本样式(三)
- 我是如何走进黑客世界的?
- 使用Excel TRIMMEAN忽略异常值
- 进程间通讯SendMessage
- 计算机硬盘怎么设置ntfs,Windows7系统如何把磁盘格式转换为NTFS的方法
- 抖音21.8版本抓包方法(Android)
- 使用PreTranslateMessage替代钩子函数处理键盘消息
- 二维中的OBB相交测试
- 软件质量保证与测试大作业,软件测试大作业..docx
- GIT CZ的错误解决
- Node 中的 Events
- msp430发送pwm信号_使用MSP430G2单片机的PWM模块控制LED指示灯的亮度
热门文章
- Hibernate中使用Criteria查询及注解——(Dept.hbm.xml)
- SpringMVC的视图解析器
- 2020蓝桥杯省赛---java---B---4( 合并检测)
- mysql---批量插入数据:100w条数据
- java for遍历hashmap_Java 使用for和while循环遍历HashMap的方法及示例代码
- mysql自动插入的时间不对 差8小时
- java虚拟机的内存模型_JVM(Java虚拟机)内存模型(转载/整理)
- linux写入二进制文件内容,linux – 从管道读取数据并写入标准输出,中间延迟.必须处理二进制文件...
- compose应用_带有PostgreSQLDocker Compose for Spring Boot应用程序
- 螺旋测微器 flash_使用测微计收集应用程序指标