如何使用 Java 注释实现域强类型

问题描述

问题

使用存储为 Long 的多个外键的贫血域对象。试图防止使用某种强类型域类型转置值。

例如给定以下产品评论类:

public class Review {
  private Long id;
  private Long productId;
  private int rating;
  private String title;
  private String body;
  private Long createdById;
  private Date createdAt;
  //...
}

我想防止自己不小心将错误的外键转换为任何 Long 的外键:

ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // see transpose error here as typical type checking doesn't catch it

解决方

继承

一个明显的解决方案是为域类型实现一个简单的类层次结构。

public class LongId {
  private Long id;
  //...
}
//...
public class ReviewId extends LongId {...}
public class ProductId extends LongId {...}
//...
public class Review {
  private ReviewId id;
  private ProductId productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // compile error as types don't match

解决方案的缺点是包含实际类型,因此很难将其编组到/从 JSON 编组到/从 JSON 编组到数据库/从数据库编组,这需要我编写大量自定义序列化程序。

泛型

我见过的另一个解决方案是使用泛型,但增加了冗长的语法,并且仍然必须编写自定义序列化程序才能获得简单的类型。

public class LongId<T> {
  private Long id;
  //...
}
//...
public interface ReviewId {}
public interface ProductId {}
//...
public class Review {
  private LongId<ReviewId> id;
  private LongId<ProductId> productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // compile error as types don't match

注释?

有人用 Java 注释解决这个问题吗?涉及哪些内容 一旦您了解了 hello world 示例,Java 注释的文档环境就会变得稀少。我发现的只是一个传递参考,即系统是可插入的,我必须编写自己的 maven 插件来进行类型检查。我对此非常感兴趣,因为我不希望在其他样板文件中编写自定义序列化程序,因为这些类型只是大多数 JSON 和数据库库都大力支持的普通 Java 引用类型。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD,ElementType.LOCAL_VARIABLE,ElementType.ParaMETER })
@TypeQualifier(applicableto = Long.class)
public @interface ReviewId {}
//...
public class Review {
  private @ReviewId Long id;
  private @ProductId Long productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // **magic** happens here so that both maven and IDE catch this at compile time

解决方法

根据您的要求,Checker Framework 是一种基于注释的方法。

Checker Framework 允许您使用类型注释指定域属性,并在编译时强制执行这些属性。 亚马逊、谷歌、优步等许多公司都在使用它。

您可以使用 pre-built type system,也可以使用 build your own,它可以只需要 4 行代码 (example)。您不需要编写 Maven 插件。

有关详细信息,请参阅 the Checker Framework manual