【问题标题】:Overriding generic varargs function with a specific one用特定的覆盖泛型可变参数函数
【发布时间】:2020-08-11 15:30:06
【问题描述】:

我正在尝试编写一个库函数(f),它在其实现中使用另一个权重函数(w)。 我希望有一个默认的权重函数(dw)在使用中,同时也允许库函数(f)的用户提供他们自己的权重函数(w)。

我为具有计算功能的权重函数制作了一个接口。但是,因为我不知道这样的函数需要什么参数,所以我这样定义它:

public interface WeightFunction {
    double calculate(Object ... arguments);
}

但是,当我用我的默认函数(dw)覆盖它时,我做了一件丑陋的事情:

 @Override
    public double calculate(Object ... arguments) {
        return calculate((Pixel)arguments[0], (Pixel)arguments[1]);
    }

    public double calculate(Pixel u, Pixel v){
        //logic here
        return 0;
    }

有没有更优雅的方法来做到这一点?这算不算好的形式?

【问题讨论】:

  • 如果argument[0]argument[1] 不是instanceof Pixel 的对象,此代码将在运行时抛出ClassCastException。这是故意的吗?
  • 如果使用默认权重函数,是的。 Pixel 是一个接口,所以如果它们不是 implements Pixel 的对象,它应该抛出一个类异常
  • ...我正在尝试编写一个使用另一个权重函数(w)的库函数(f)...“ -​​ 一个粗略的草图 你所说的“function(f) 使用 function(w)”会很有帮助。请分享你的 function(f) 的伪代码 - 或其他东西? TIA。

标签: java generics interface variadic-functions


【解决方案1】:

您可能想使用泛型?

public interface WeightFunction<T> {
    double calculate(T ... arguments);
}

class A implements WeightFunction<Pixel> {

    @Override
    public double calculate(Pixel... arguments) {
        return calculate(arguments[0], arguments[1]);
    }

    public double calculate(Pixel u, Pixel v){
        //logic here
        return 0;
    }
}

您也可以只使用一个参数并允许调用者将他的所有参数包装在一个类中。如果您有多种不同类型的参数,这可能会更好。

public interface WeightFunction<T> {
    double calculate(T argument);
}

@Override
public double calculate(SeededPixels arg) {
    return calculate(arg.u, arg.v); // * arg.seed
}

class SeededPixels {
    public final Pixel u;
    public final Pixel v;
    public final long seed;

    SeededPixels(Pixel u, Pixel v, long seed) {
        this.u = u;
        this.v = v;
        this.seed = seed;
    }
}

【讨论】:

  • 这将限制权重函数能够 T 相同类型的参数。如果权重函数采用两个像素和一个用长表示的随机种子怎么办?
  • 您可能想考虑只使用一个参数并提供一个包装不同类型的多个字段的类。在我看来,这比列出不同类型的参数要干净得多。
  • 看起来不错。因此,为了保持我的函数和参数之间的解耦,该类将没有字段或方法,对吗?它仅作为一个空容器存在,用于存放未来用户将使用的任何参数。
  • 或多或少是的。典型的方法可能是用户创建一个简单的值类,其中所有字段都是最终的。方法不是绝对必要的,字段可以是公共的,但如果他们愿意,它们也可以提供 getter 方法。当然具体的计算函数需要知道如何访问这些字段。
  • 我已将此标记为正确,因为它最好地回答了我的问题。但是我认为我的想法是不好的做法,我应该改变我的总体设计。感谢您的意见!
【解决方案2】:

泛型是要走的路。但在你的问题中解释这一点:

但是,因为我不知道这样的函数需要什么参数,所以我这样定义:

以及您对answer from Gregor Koukkoullis 的第一条评论我认为您的问题是您只需要(并且应该)声明每个采用不同数量参数的方法。没有其他办法,但无论如何这种方式更清楚。

所以你应该有这样的东西:

public interface WeightFunction<T> {
    double calculate(T... arguments);
    double calculate(Long seed, T... arguments);
    double calculate(Long seed, Integer somethingElse, T... arguments);
}

为什么可变参数必须是最后一个? See this。公认的答案可能不是最清楚的,但很少有人能澄清问题。

现在,当您在示例中实现 calculate 时,您知道参数是什么?

@Override
public double calculate(Object ... arguments) {
    // you know here that the 2 first are pixels, dont you?
    return calculate((Pixel)arguments[0], (Pixel)arguments[1]);
}

因此,使用相同的知识,您可以在界面中创建所需属性的声明。甚至可能:

double calculate(Long seed, T t1, T t2);

如果更有可能只有两个Ts。

你的问题的答案:

这算是好的形式吗?

IMO 从来都不是一个好习惯,让函数接受Objects 的数组,然后你实现一个方法来解释它想要的参数并做它想要的。我认为它强烈反对整个界面的想法。

声明“告诉”它们在做什么的方法总是更好的主意,然后在需要时添加一个新的方法声明或重构你的接口和已经实现的方法。

如果您选择在需要灵活性的任何时候传递“对象列表”,那么您很快就会陷入其中。

现在这可能会引发一个问题,我必须实现这个接口的所有方法吗?是的,但是如果您不想这样做,您可以定义单独的接口并让您的类实现其中的一个或多个或/和使用extends 来创建接口层次结构。

【讨论】:

  • 感谢您的详细回复。我决定限制我的界面范围,因为我认为我想到的设计是有缺陷的。你的接口方法是正确的,这就是为什么我最终放弃了我对接口的非常笼统的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 2018-06-20
相关资源
最近更新 更多