- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在编写一个流畅的 API 来配置和实例化一系列“消息”对象。我有一个消息类型的层次结构。
为了在使用 fluent API 时能够访问子类的方法,我使用泛型对子类进行参数化,并使所有 fluent 方法(以“with”开头)返回泛型类型。请注意,我省略了 fluent 方法的大部分主体;他们进行了很多配置。
public abstract class Message<T extends Message<T>> {
protected Message() {
}
public T withID(String id) {
return (T) this;
}
}
具体的子类类似地重新定义了泛型。
public class CommandMessage<T extends CommandMessage<T>> extends Message<CommandMessage<T>> {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public T withCommand(String command) {
return (T) this;
}
}
public class CommandWithParamsMessage extends
CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, paramValue);
return this;
}
}
此代码有效,即我可以实例化任何类并使用所有流畅的方法:
CommandWithParamsMessage msg = CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
以任何顺序调用流利的方法是这里的主要目标。
但是,编译器警告所有 return (T) this
都是不安全的。
Type safety: Unchecked cast from Message to T
我不确定如何重新组织层次结构以使这段代码真正安全。尽管它有效,但以这种方式使用泛型确实令人费解。特别是,如果我忽略警告,我无法预见会发生运行时异常的情况。会有新的消息类型,所以我需要保持代码的可扩展性。如果解决方案是完全避免继承,我还想获得替代建议。
有other questions在 SO 上解决了类似的问题。他们指出了一个解决方案,其中所有中间类都是抽象的,并声明了一个像 protected abstract self()
这样的方法。不过,到头来还是不安全的。
最佳答案
您的代码基本上是对泛型的不安全使用。例如,如果我编写了一个扩展消息的新类,比如 Threat,并有一个新方法 doSomething(),然后我创建了一个由这个新类参数化的消息,它创建了一个 Message 的实例,然后尝试对其进行转换到它的子类。但是,由于它是 Message 的实例,而不是 Threat 的实例,因此尝试调用此消息将导致异常。因为 Message 不可能 doSOthing()。
此外,这里也没有必要使用泛型。普通的旧继承可以正常工作。由于子类型可以通过使其返回类型更具体来覆盖方法,因此您可以:
public abstract class Message {
protected Message() {
}
public Message withID(String id) {
return this;
}
}
然后
public class CommandMessage extends Message {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public CommandMessage withCommand(String command) {
return this;
}
}
这可以正常工作,前提是您以正确的顺序调用参数:
CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
会失败,但是
CommandWithParamsMessage.newMessage().withParameter("arg", "value")
.withCommand("doAction").withID("do")
会成功,因为它只是“向上类型”,最后返回一个“消息”类。如果你不希望它“uptype”,那么只需覆盖继承的命令,现在你可以按任何顺序调用方法,因为它们都返回原始类型。
例如
public class CommandWithParamsMessage extends
CommandMessage {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, paramValue);
return this;
}
@Override
public CommandWithParamsMessage withCommand(String command){
super.withCommand(command);
return this;
}
@Override
public CommandWithParamsMessage withID(String s){
super.withID(s);
return this;
}
}
现在您将使用上述两个流畅调用中的任何一个流畅地返回 CommandWithParamsMessage。
这是否解决了您的问题,还是我误解了您的意图?
关于java - 具有继承和泛型的流畅 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23731270/
这是代码片段。 请说出这种用小内存存储大数据的算法是什么。 public static void main(String[] args) { long longValue = 21474836
所以我使用 imap 从 gmail 和 outlook 接收电子邮件。 Gmail 像这样编码 =?UTF-8?B?UmU6IM69zq3OvyDOtc68zrHOuc67IG5ldyBlbWFpb
很久以前就学会了 C 代码;想用 Scheme 尝试一些新的和不同的东西。我正在尝试制作一个接受两个参数并返回两者中较大者的过程,例如 (define (larger x y) (if (> x
Azure 恢复服务保管库有两个备份配置选项 - LRS 与 GRS 这是一个有关 Azure 恢复服务保管库的问题。 当其驻留区域发生故障时,如何处理启用异地冗余的恢复服务保管库?如果未为恢复服务启
说,我有以下实体: @Entity public class A { @Id @GeneratedValue private Long id; @Embedded private
我有下一个问题。 我有下一个标准: criteria.add(Restrictions.in("entity.otherEntity", getOtherEntitiesList())); 如果我的
如果这是任何类型的重复,我会提前申请,但我找不到任何可以解决我的具体问题的内容。 这是我的程序: import java.util.Random; public class CarnivalGame{
我目前正在使用golang创建一个聚合管道,在其中使用“$ or”运算符查询文档。 结果是一堆需要分组的未分组文档,这样我就可以进入下一阶段,找到两个数据集之间的交集。 然后将其用于在单独的集合中进行
是否可以在正则表达式中创建 OR 条件。 我正在尝试查找包含此类模式的文件名列表的匹配项 第一个案例 xxxxx-hello.file 或者案例二 xxxx-hello-unasigned.file
该程序只是在用户输入行数时创建菱形的形状,因此它有 6 个 for 循环; 3 个循环创建第一个三角形,3 个循环创建另一个三角形,通过这 2 个三角形和 6 个循环,我们得到了一个菱形,这是整个程序
我有一个像这样的查询字符串 www.google.com?Department=Education & Finance&Department=Health 我有这些 li 标签,它们的查询字符串是这样
我有一个带有静态构造函数的类,我用它来读取 app.config 值。如何使用不同的配置值对类进行单元测试。我正在考虑在不同的应用程序域中运行每个测试,这样我就可以为每个测试执行静态构造函数 - 但我
我正在寻找一个可以容纳多个键的容器,如果我为其中一个键值输入保留值(例如 0),它会被视为“或”搜索。 map, int > myContainer; myContainer.insert(make_
我正在为 Web 应用程序创建数据库,并正在寻找一些建议来对可能具有多种类型的单个实体进行建模,每种类型具有不同的属性。 作为示例,假设我想为“数据源”对象创建一个关系模型。所有数据源都会有一些共享属
(1) =>CREATE TABLE T1(id BIGSERIAL PRIMARY KEY, name TEXT); CREATE TABLE (2) =>INSERT INTO T1 (name)
我不确定在使用别名时如何解决不明确的列引用。 假设有两个表,a 和 b,它们都有一个 name 列。如果我加入这两个表并为结果添加别名,我不知道如何为这两个表引用 name 列。我已经尝试了一些变体,
我的查询是: select * from table where id IN (1,5,4,3,2) 我想要的与这个顺序完全相同,不是从1...5,而是从1,5,4,3,2。我怎样才能做到这一点? 最
我正在使用 C# 代码执行动态生成的 MySQL 查询。抛出异常: CREATE TABLE dump ("@employee_OID" VARCHAR(50)); "{"You have an er
我有日期 2016-03-30T23:59:59.000000+0000。我可以知道它的格式是什么吗?因为如果我使用 yyyy-MM-dd'T'HH:mm:ss.SSS,它会抛出异常 最佳答案 Sim
我有一个示例模式,它的 SQL Fiddle 如下: http://sqlfiddle.com/#!2/6816b/2 这个 fiddle 只是根据 where 子句中的条件查询示例数据库,如下所示:
我是一名优秀的程序员,十分优秀!