问题描述
问题
使用存储为 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。