- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个流畅的 API 来配置和实例化一系列“消息”对象。我有一个消息类型层次结构。
为了在使用 Fluent API 时能够访问子类的方法,我使用泛型来参数化子类,并使所有 Fluent 方法(以“with”开头)返回泛型类型。请注意,我省略了 fluid 方法的大部分主体;其中进行了很多配置。
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/56603128/
我是一名优秀的程序员,十分优秀!