【问题标题】:Abstract method with variable list of arguments具有可变参数列表的抽象方法
【发布时间】:2013-07-30 01:39:10
【问题描述】:

我还没有找到解决这个问题的优雅方法。我有一个抽象类,其他几个类使用抽象方法继承该抽象类,该抽象方法可以包含从零到 4-5 个不同类型的参数。

public abstract class Item {
public abstract void use();
}

例如,我有一个 Book 类继承了 this 并且在覆盖 use() 时不接受任何参数,我有一个 Key 类在覆盖时继承并接受一个 String 和一个 Queue 作为参数等等......

我尝试过使用泛型,但是当它实际上取决于类时,我必须输入使用的数字,例如 Item。

public abstract class Item<T,U> {
public abstract void use(T arg1, U arg2); //Number of arguments/types could be more or less
}

我尝试发送对象的变量列表,但对象类型始终是可变的,我不确定在继承类中接收的语法。

public abstract class Item<T> {
public abstract void use(T... arguments);
}

public class Book extends Item<?> {
public void use(?);
}

public class Book extends Item<String, Queue> { //Wrong number of arguments since I can't use Item<T...>
public void use(String str, Queue q); //fails
}

我可能只是做错了什么 - 任何人都可以提供任何帮助或见解吗?

【问题讨论】:

  • 如果你这样做Item it = new Book();,如果编译器不知道正确的参数类型是什么,你如何建议在其上调用方法Book.use(String,Queue);?如果您不希望在父类的变量中拥有Item 的子类,为什么该方法需要是抽象的?

标签: java inheritance variadic-functions


【解决方案1】:

我一直在为同样的问题而苦苦挣扎,并且没有一个完美的答案,但我可以给你一些考虑的事情。首先,您基本上是在尝试做一些本质上与面向对象编程相反的事情,即您正在尝试创建一个变量接口。接口的重点是获取对象的抽象版本(例如,Item 而不是 Book)的代码知道如何调用 use() 方法。这意味着他们必须知道可以传递给 use() 方法的内容。如果答案取决于抽象类或接口的实现,那么您需要确保使用它的代码实际上知道它正在使用哪种实现(Book 等),否则它不会知道如何调用 use () 无论如何都要使用适当的参数。老实说,听起来您需要重构代码。

但是,有一种方法可以在不重构架构的情况下回答您的问题。您可以创建一个类,其数据是可能传递给 use() 方法的所有不同类型的参数,让调用代码设置该类的字段,然后将其传递给 use() 方法。例如:

public class UseParameters {
    private String string;
    private Queue queue;
    // Any other potential parameters to use(...)

    public void setString(String string) {
        this.string = string;
    }

    public String getString() {
        return string;
    }

    // All of the other accessor methods, etc.
}

然后,你可以像这样在Item中定义使用方法:

public abstract void use(UseParameters params);

并且任何使用 Item 的代码都必须适当地设置对象的参数:

Item item = // However you're going to get the item
UseParameters params = new UseParameters();
params.setString("good string");
params.setQueue(new Queue());
item.use(params);

我只想指出,如果上面的代码知道 Item 是一本书(这就是它知道设置字符串和队列的方式,那么为什么不直接获取 Book 并跳过需要使用变量的抽象类() 方法?但我跑题了。无论如何,Book 会像这样实现 use() 方法:

@Override
public void use(UseParameters params) {
    if(params.getString == null || params.getQueue() == null)
        // throw exception

    // Do what books do with strings and queues
}

我认为这可以满足您的需求,但我认为您应该考虑重构。

【讨论】:

  • 感谢您的详细解答。我仍然处于可以重新设计方法的地步——这似乎是最好的选择。可能有足够的相似性来组合成几个接口,如下文所述。如果不是这样,放弃界面并确保自己包含使用可能是有意义的。
【解决方案2】:

你想要的是Value Object Pattern

定义一个类,将各种参数类型封装到一个值对象中,并让抽象方法接受这种类型的参数。您正在考虑的每个参数变体都有自己的值类。

然后简单地为类添加一个泛型类型并让抽象方法接受该类型的参数:

public abstract class Item<V> {
    public abstract void use(V v);
}

要使用它,假设MyItem 需要一个MyValueClass 类型的值对象:

public class MyItem extends Item<MyValueClass> {
    public void use(MyValueClass v) {
    }
}

【讨论】:

  • 7 年后,我得到了相同的解决方案 xD。我认为这应该是公认的答案,因为它使用我发现比原始解决方案更干净的泛型类型。就我而言,查询/命令模式 (CQRS) 需要它,因此我需要一个接口作为我创建的命令/查询的指南。没有比这个解决方案更好的了。每次我想在 qry/cmd 中使用多个参数时都必须创建一个新类有点破坏性(如果只有一个,您只需将该类型作为泛型传递)。
【解决方案3】:

如果用作参数的类型总是可变的,我看不出使用泛型的理由。只需使用普通的 Object 类型:

public abstract class Item {
    public abstract void use(Object ... arguments);
}

public class Book extends Item {
    public void use(Object ... arguments) { ... }
}

【讨论】:

    【解决方案4】:

    我能想到的最好方法是根据它们的use() 方法的行为对项目进行分组。

    示例

    public abstract class QueueableItem {
        public abstract void use(String, Queue);
    }
    
    public abstract class OrdinaryItem{
        public abstract void use(String);
    }
    

    如果分组的项目共享一个共同的行为(在相同的方法签名和返回值中很常见),您可以定义和扩展一个包含该共同行为定义的父类。

    【讨论】:

      【解决方案5】:

      是的,我们可以为抽象方法提供参数,但必须为我们在派生类中编写的实现方法提供相同类型的参数。

      【讨论】:

        猜你喜欢
        • 2017-11-04
        • 1970-01-01
        • 2011-03-10
        • 2015-01-19
        • 2011-06-04
        • 1970-01-01
        • 1970-01-01
        • 2011-09-08
        • 2013-04-07
        相关资源
        最近更新 更多