Boj*_*Boj 31 java generics fluent-interface
我正在编写一个流畅的API来配置和实例化一系列"消息"对象.我有一个消息类型的层次结构.
为了能够在使用流畅的API时访问子类的方法,我使用泛型来参数化子类,并使所有流畅的方法(以"with"开头)返回泛型类型.请注意,我省略了流体方法的大部分主体; 其中有很多配置.
public abstract class Message<T extends Message<T>> {
protected Message() {
}
public T withID(String id) {
return (T) this;
}
}
Run Code Online (Sandbox Code Playgroud)
具体子类同样重新定义泛型类型.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码有效,即我可以实例化任何类并使用所有流畅的方法:
CommandWithParamsMessage msg = CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
Run Code Online (Sandbox Code Playgroud)
以任何顺序调用流畅的方法是这里的主要目标.
但是,编译器警告所有return (T) this都不安全.
类型安全:从Message到T取消选中
我不确定如何重新组织层次结构以使此代码真正安全.尽管它有效,但以这种方式使用仿制药确实令人费解.特别是,如果我忽略警告,我无法预见会发生运行时异常的情况.将有新的消息类型,所以我需要保持代码可扩展.如果解决方案是完全避免继承,我也想获得替代方案的建议.
此处还有其他 问题可解决类似问题.他们指出一个解决方案,其中所有中间类都是抽象的,并声明一个类似的方法protected abstract self().最后,它还不安全.
phi*_*686 19
您的代码从根本上说是对Generics的不安全使用.例如,如果我编写一个扩展消息的新类,请说Threat,并且有一个新方法doSomething(),然后我创建一个由这个新类参数化的消息,它创建一个Message实例,然后尝试转换它到它的子类.但是,由于它是Message的实例,而不是威胁,尝试调用此消息将导致异常.因为Message不能doSOmething().
此外,它也没有必要在这里使用泛型.普通的旧继承会很好.由于子类型可以通过使返回类型更具体来覆盖方法,因此您可以:
public abstract class Message {
protected Message() {
}
public Message withID(String id) {
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
然后
public class CommandMessage extends Message {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public CommandMessage withCommand(String command) {
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
这可以正常工作,因为您可以按正确的顺序调用参数:
CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
Run Code Online (Sandbox Code Playgroud)
会失败,但是
CommandWithParamsMessage.newMessage().withParameter("arg", "value")
.withCommand("doAction").withID("do")
Run Code Online (Sandbox Code Playgroud)
将成功,因为它只"up类型",最后返回一个"消息"类.如果你不想"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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您将使用上面两个流畅的调用中的任何一个流畅地返回CommandWithParamsMessage.
这会解决您的问题,还是我误解了您的意图?
Wil*_*ice 14
我之前做过类似的事.它可能变得丑陋.事实上,我尝试过的次数比我用过的次数多; 通常它会被删除,我试图找到一个更好的设计.也就是说,为了帮助你在路上走得更远,试试这个:
让您的抽象类声明一个方法,如:
protected abstract T self();
Run Code Online (Sandbox Code Playgroud)
这可以this在您的return语句中替换.子类将需要返回与绑定匹配的内容T- 但不保证它们返回相同的对象.
如果您更改这样的签名,您既不应该收到任何警告,也不需要任何演员:
abstract class Message<T extends Message<T>> {
public T withID(String id) {
return self();
}
protected abstract T self();
}
abstract class CommandMessage<T extends CommandMessage<T>> extends Message<T> {
public T withCommand(String command) {
// do some work ...
return self();
}
}
class CommandWithParamsMessage extends CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName, String paramValue) {
// do some work ...
return this;
}
@Override protected CommandWithParamsMessage self() {
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8671 次 |
| 最近记录: |