如何使用 Java 注释实现域强类型
How to implement domain strong typing using Java annotations
问题
使用存储为 Long 的多个外键的贫血域对象。试图防止使用某种强类型域类型转置值。
例如给定以下产品评论 class:
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
解决方案
继承
一个明显的解决方案是为域类型实现一个简单的 class 层次结构。
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
此解决方案的缺点是包含实际类型,因此编组它很费力 to/from JSON 和 in/out 数据库需要我编写大量自定义序列化程序.
泛型
我见过的另一种解决方案是使用泛型,但添加了冗长的语法,并且仍然必须编写自定义序列化程序才能获得简单的类型。
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 插件来进行类型检查。我对此作为一种可能的解决方案非常感兴趣,因为我不希望在其他样板中编写自定义序列化程序,因为这些类型只是普通的 Java 引用类型,大多数 JSON 都大力支持和数据库库。
@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 允许您使用类型注释指定域属性,并在编译时强制执行这些属性。
它被用于亚马逊、Google、优步等公司。
您可以使用 pre-built type system, or you can build your own which can be as little as 4 lines of code (example)。您不需要编写 Maven 插件。
有关详细信息,请参阅 the Checker Framework manual。
问题
使用存储为 Long 的多个外键的贫血域对象。试图防止使用某种强类型域类型转置值。
例如给定以下产品评论 class:
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
解决方案
继承
一个明显的解决方案是为域类型实现一个简单的 class 层次结构。
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
此解决方案的缺点是包含实际类型,因此编组它很费力 to/from JSON 和 in/out 数据库需要我编写大量自定义序列化程序.
泛型
我见过的另一种解决方案是使用泛型,但添加了冗长的语法,并且仍然必须编写自定义序列化程序才能获得简单的类型。
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 插件来进行类型检查。我对此作为一种可能的解决方案非常感兴趣,因为我不希望在其他样板中编写自定义序列化程序,因为这些类型只是普通的 Java 引用类型,大多数 JSON 都大力支持和数据库库。
@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 允许您使用类型注释指定域属性,并在编译时强制执行这些属性。 它被用于亚马逊、Google、优步等公司。
您可以使用 pre-built type system, or you can build your own which can be as little as 4 lines of code (example)。您不需要编写 Maven 插件。
有关详细信息,请参阅 the Checker Framework manual。