当前位置: 首页 > 面试题库 >

具有继承和泛型的Fluent API

明正德
2023-03-14
问题内容

我正在编写一个流畅的API,以配置和实例化一系列“消息”对象。我有消息类型的层次结构。

为了在使用Fluent API时能够访问子类的方法,我使用了泛型来对子类进行参数化,并使所有的fluent方法(以“
with”开头)都返回泛型类型。注意,我省略了流利方法的大部分内容。其中进行了许多配置。

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都不安全。

类型安全:未经检查的从Message到T的转换

我不确定如何重组层次结构以使此代码真正安全。即使可行,以这种方式使用泛型确实让人费解。特别是,如果我只是忽略警告,我将无法预见运行时异常将发生的情况。将会有新的消息类型,因此我需要保持代码的可扩展性。如果解决方案是完全避免继承,那么我也想获得其他选择的建议。

SO上还有其他
问题可以解决类似的问题。它们指向所有中间类都是抽象的并声明类似的方法的解决方案protectedabstract self()。尽管如此,最终还是不安全的。


问题答案:

从根本上说,您的代码是对泛型的不安全使用。例如,如果我编写一个扩展消息的新类(例如Threat),并具有一个新方法doSomething(),然后我创建一个由该新类参数化的消息,并创建Message的实例,然后尝试对其进行投射到其子类。但是,由于它是Message的实例,而不是Threat的实例,因此尝试调用此消息将导致异常。由于Message不可能执行doSOmething()。

此外,这里也不必使用泛型。普通的旧继承可以正常工作。由于子类型可以通过使返回类型更具体来覆盖方法,因此您可以:

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 {

    html" target="_blank">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以及上面两个流畅的调用之一。

这是否解决了您的问题,还是我误解了您的意图?



 类似资料:
  • 问题内容: 我有三节课: 现在在我的主要职能中,我做这样的事情: 但这会产生2个编译时错误: 无法将A转换为fileHandler 无法将B转换为fileHandler 如果基类不是泛型的,那么我如何摆脱这些错误呢? 更新: 我的班级层次结构是: 调用函数的C类或 调用函数的D类。 C和D都从抽象类E派生。 现在,我应该如何在C和D以及E中定义这些功能: 我给出了以下内容: E: C: D: C,

  • 问题内容: 我正在使用具有继承性的流畅接口。我声明基类Constructor受保护,因此您不能创建Foo ,这会导致在调用add()时引发ClassCastException。但是我在返回一个新的Foo实例的静态方法上遇到了麻烦。 这主要是流利的接口,特定领域的语言和泛型方面的一项练习(个人而非家庭作业),所以请不要问我需要什么。 编辑:Eclipse错误 问题答案: 本质上,您有一个递归类型声明

  • 和这个问题相关的课程: 现在,这似乎已被编译器接受。不幸的是,我不知道/理解如何创建的新实例,。 当然,这确实不起作用:

  • 为了在 .NET Core 中使用依赖关系注入,我们构建了一堆存储库接口,供控制器用于数据库交互。 我们有一个< code>EntityBase类,其中包含一些方法,我们的通用存储库接口使用这个基类,例如:< code>IRepository 我想添加一个更具体的 类,该类扩展了 ,以表示我们有一些要按标记筛选的实体。我希望 有一个抽象属性,我可以在控制器中使用该属性,以便我可以抽象出来并重用过滤

  • 我有2个抽象的类操纵泛型和一个工厂与泛型。父级独立于trips对象和因式分解代码。儿子只基于trips。 Eclipse上的AbstractTripBasedPurchaseExtractor第9行出现错误:Bound mismatch:类型TRIP_PURCHASE不是bounded参数的有效替代品 类型的 此处:extends AbstractPurchaseExtractor“ 在son类上

  • 我有一个典型的问题,什么是更好的,我认为答案总是视情况而定,但我还是想澄清一下。所以有两种方法: 逻辑是: 我知道由于类型擦除,方法签名存在冲突,所以我的问题不是“为什么我不能同时拥有这两个方法?”,而是“你会选择哪种方法?”。