这是有关Manifold的系列文章的第二篇, Manifold是Java的一种新型工具。 第1部分介绍Type Manifold API ,它是常规代码生成的强大替代方案。 本部分探讨了扩展类 ,它是一种创新功能,使您可以使用自己的方法,接口和其他功能来补充类,而无需子类化而无需更改原始类。
快! 编写一些代码以将File
的内容读取为String
。 预备,开始!
作为务实的开发人员,您希望这样的事情:
String contents = file.readText();
遗憾的是,您键入file.
在您的IDE中并Swift发现不存在这种方法。 接下来,您在StackOverflow中搜索样板解决方案,并找到有用的代码段。 您希望避免自己和其他人重复此工作,因此将样例代码片段包装在Util
库中:
public class MyFileUtil {
public static String readText(File file) {
// boilerplate code...
}
}
现在您可以编写:
String contents = MyFileUtil.readText(file);
这是一样好吗?
您应该拥有一个更友好,更实用的File
API — readText()
更适合作为直接在File
上的实例方法,并且更易于发现。 这是语言社区中通常称为“ Extension Methods
所有差异。 这也是Manifold在Java遗留下来的地方接手的地方。
集成块通过扩展类完全实现了Java的扩展方法:
package extensions.java.io.File;
import manifold.ext.api.*;
@Extension
public class MyFileExtension {
public static String readText(@This File thiz) {
// boilerplate code...
}
}
MyFileExtension
用readText()
作为实例方法补充File
:
String contents = file.readText();
实用主义者正是您想要的!
此外, IntelliJ IDEA为歧管提供了全面的支持。 您可以使用代码完成之类的功能轻松发现和使用扩展方法:
看到它在行动。 创建新的扩展类,对其进行重构,查找用法等:扩展类的剖析
扩展类易于使用简单的约定和注释来实现:
package extensions.java.io.File;
扩展类的程序包名称必须以extensions.
名结尾extensions.
。 使用Java 8,您可以将所有扩展类植根于extensions
包中。 在Java 9或更高版本中,如果使用显式模块,则必须在模块名称之前添加软件包名称,例如package foo.extensions.java.io.File
,其中foo
是模块名称。 为了在依赖关系中维护唯一的名称,通常最好还是进一步限定扩展类。
@Extension
public class MyFileExtension {
扩展类必须带有@Extension
注释,这有助于Manifold快速识别项目中的扩展类。
public static String readText(@This File thiz) {
必须将所有扩展方法声明为“静态”,稍后再进行介绍。 作为调用的接收者,扩展实例方法的第一个参数必须与扩展类(在本例中为File
具有相同的类型。 参数名称thiz
是常规名称,您可以使用任何喜欢的名称。
通常情况下就是这样。
静态方法
您可以定义static
扩展方法,如下所示:
@Extension
public static FileSystem getLocalFileSystem() {
return FileSystems.getDefault();
}
由于静态方法没有接收者,因此方法本身必须使用@Extension
进行注释,以便歧管可以这样标识它。
像调用普通静态方法File
一样调用它:
File.getLocalFileSystem()
泛型
您也可以为通用类进行扩展,并定义通用扩展方法。 这就是Manifold扩展库与集合和其他泛型类一起工作的方式。 例如,这是Iterable
的first()
扩展方法:
java
public static <T> T first(@This Iterable<T> thiz, Predicate<T> predicate) {
for (T element: thiz) {
if (predicate.test(element)) {
return element;
}
}
throw new NoSuchElementException();
}
注意,该扩展名是与扩展类具有相同类型变量名称的通用方法: Iterable
T
由于扩展方法是静态的,因此这是将类型变量从扩展类传递到扩展方法的方式。
要定义你追加方法的类型变量来扩展类的类型变量列表中选择一个通用的扩展方法。 流形的map()
extension illustrates this format:
public static <E, R> Stream<R> map(@This Collection<E> thiz, Function<? super E, R> mapper) {
return thiz.stream().map(mapper);
}
map
是一种通用扩展方法,具有R
类型并传递Collection
的类型变量E
静态调度
扩展类不会在物理上改变其扩展类; 扩展中定义的方法并未真正插入扩展类中。 取而代之的是,Java编译器和Manifold协作以对扩展中的静态方法进行调用,就像对扩展类上的实例方法的调用一样。 结果,扩展调用以静态方式调度。 因此,与虚拟方法调用不同的是,始终根据接收者的编译时类型进行扩展调用。
静态调度的另一个结果是,即使扩展对象的null
在调用站点为null
,扩展方法也可以接收调用。 歧管扩展库利用此功能来提高可读性和null安全性。 例如, CharSequence.isNullOrEmpty()
将接收者的值与null进行比较,因此您不必:
public static boolean isNullOrEmpty(@This CharSequence thiz) {
return thiz == null || thiz.length() == 0;
}
String name = null;
if (name.isNullOrEmpty()) {
println("empty");
}
这里的示例不检查是否为空,而是将负担转移给扩展。
辅助功能和范围
扩展方法从不遮盖或覆盖类方法; 当扩展方法的名称和参数与类方法匹配时,该类方法始终优先于扩展名。 例如:
public class Tree {
public void kind() {
println("evergreen");
}
}
public static void kind(@This Tree thiz) {
println("binary");
}
扩展方法永远不会赢,对kind()
的调用总是显示"evergreen"
。 此外,如果在编译时Tree
和扩展名如示例中所示发生冲突,则编译器会在扩展类中警告冲突。
扩展方法仍可以_overload_一个类方法,其中方法名称相同,但参数类型不同:
public class Tree {
public void harvest() {
println("nuts");
}
}
public static void harvest(@This Tree thiz, boolean all) {
println(all ? "wood" : thiz.harvest());
}
调用tree.harvest(true)
打印“ wood”。
扩展库
扩展库是由一组扩展类定义的功能的逻辑分组。 Manifold包含几个常用类的扩展库,其中许多是从Kotlin扩展改编而来的。 每个库都可以作为单独的模块或Jar文件提供,您可以根据需要将其分别添加到项目中。
馆藏
该库在模块“ manifold-collections”中定义:
- java.lang.Iterable
- java.util.Collection
- java.util.List
- java.util.stream.Stream
文本
该库在模块“ manifold-text”中定义:
- java.lang.CharSequence
- java.lang.String
输入输出
该库在模块“ manifold-io”中定义:
- java.io.BufferedReader
- java.io.File
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
网络/杰森
该库在模块“ manifold-json”中定义:
- java.net.URL
- javax.script.Bindings
摘要
扩展方法为Util库提供了强大的替代方法。 该功能在C#,Scala和Kotlin等现代语言中得到了广泛支持。 现在,通过Manifold,您可以在Java中使用扩展方法。 使用它可以通过API提高开发人员的工作效率,并受益于Manifold通用类的内置扩展库。 开始使用扩展方法和其他Manifold功能的最简单,最佳方法是通过IntelliJ IDEA的Manifold插件。
更多内容:
在本系列的后面部分,我将介绍结构化打字,这是一种强大的抽象,类似于TypeScript和Go中的接口。 结合扩展类,结构化类型使一些令人兴奋的功能成为可能,包括扩展接口和Extension by Implementation 。
翻译自: https://jaxenter.com/manifold-code-generator-part-2-151762.html