【发布时间】:2014-11-27 07:41:37
【问题描述】:
我从未使用过类型别名,但这个概念似乎是一个非常有用的功能,可以在相同类型的对象上添加语义并防止常见的拼写错误。
假设有void foo(float volume, float weight)。可以这样调用:foo(v, m),但foo(m, v) 不是一个明显的错字。 void process(Iterable<File> javaPath, Iterable<File> classPath) 可以是另一个用例。不幸的是,Java 中没有类型别名,一些变通方法确实是矫枉过正:
- 将单个字段聚合到一个类中(将原语装箱到对象中;对象中的对象具有额外的引用;更复杂的序列化/反序列化规则)
- 从另一个类扩展一个类(如how do i create some variable type alias in Java - 基元不可能;类可能是最终类或具有不可访问的构造函数)。
所以它们都有缺点和运行时/性能成本。
据我所知,在 Java 中,基于运行时的“类型别名”可能会替换为编译时检查,例如处理 @NotNull 和 @Nullable。是否有任何静态类型别名检查器/APT 工具支持 void foo(@Volume float volume, @Weight float weight) 之类的结构,以便检查器可以在编译时验证这种“类型安全”,并要求对传递的变量/常量和文字进行注释(在声明和调用站点分别)?
之所以提出这个问题,是因为我目前正在开发一个可以使用多个后端的 Java 源代码处理工具(目前,仅限 JDT)。只要我希望它与 JDT 无关,我就希望拥有类型、方法和字段的业务对象。尽管如此,我丢失了很多来自 JDT AST 的信息,我的工具目前最好的方法是使用字符串(它允许执行一些足以满足工具范围的分析)。但是像void process(String type, String field, String method) 这样的方法令人困惑,所以我创建了Type、Field 和Method,它们代表域对象并且与JDT 无关。它们很好,但我不希望它们在未来得到扩展(所有这三种类型都有单个 private final String name; 字段 + 我必须覆盖 hashCode/equals 和 toString)。这让我想到了再次对 APT 进行类型检查的想法。只要可以处理原始数据,有void process(@Type String type, @Field String field, @Method String method)之类的就足够了(我仍然可以使用单个对象)。
【问题讨论】:
-
我不明白。如果我打电话给
foo(a,b),其中a 和b 都是int,那么工具应该如何验证我的a是否是Volume?如果您需要Volume具有语义含义,请将其设为类。 -
@AdrianShum 我只需要在浮点数上提供更多语义。将单个原语包装到一个类中会导致另一个类实例化(更多堆),在序列化器/反序列化器中需要更多适配器(例如,GSON 不适用于新类——需要更多适配器)。这个想法就像 NotNull 和 Nullable 的想法一样——你创建类不只是为了添加语义,你注释,对吧? .NET 支持可以轻松保存原语而没有性能成本的值类型(它本身没有原语,一切都是真正的对象)。但是Java中没有值类型。
-
除非你能告诉我它如何检查输入
int是否是Volume,否则我认为在这种情况下使用注释不是一个可行的想法。如果您正在寻找减少意外拼写错误的方法,则可能是您想要的类似 fluent-builder 的模式:foo().withVolume(a).withWeight(b).run()之类的东西。当然还有额外的开发工作,但如果方法参数真的那么模棱两可且容易混淆,那么额外的努力是值得的。而且,C# 中的值对象并不能解决问题。是的,从堆栈实例化的开销较小,但是 -
如果我的理解是正确的,仍然会有额外的开销。 Java 中没有针对此问题的直接解决方案。如果情况真的那么糟糕,那么该包装类的额外性能损失应该不是什么大问题。有很多方法可以减少额外开销:享元、工厂方法、不可变 obj 等。
-
嗯....我有点困惑。使用 fluent-builder-like 模式不会使您的模块相互依赖。这只是编写方法的另一种方式
标签: java type-safety annotation-processing type-alias