【问题标题】:How to enforce child class behavior/methods when the return types of these methods depends on the child class?当这些方法的返回类型取决于子类时,如何强制执行子类行为/方法?
【发布时间】:2021-08-06 18:16:50
【问题描述】:

我想要达到的目标:

我正在尝试编写一个抽象类,它是所有子类都应具有的数据类型和行为的蓝图。但是,这些强制方法的返回类型将取决于子类本身。

我的尝试:

我在 cmets 中被告知我的许多封闭问题之一,我在这里所做的是重载我父母的方法而不是覆盖它们。我理解这是因为我更改了方法签名,@Override 要求签名相同,但实现可以不同。

public abstract class BaseMatrix {

    protected int[] shape;
    protected int nrows;
    protected int ncols;

    public BaseMatrix(int rows, int cols){
        this.nrows = rows;
        this.ncols = cols;
        this.shape = new int[]{nrows, ncols};
    }

    // ** here is the method I want to implement/override **
    public abstract BaseMatrix mmul(BaseMatrix other); 

我最初尝试使用泛型来解决这个问题,但发现我对它们的理解不够好,无法以这种方式实施解决方案。因此,我采用了在两个类似问题中找到的建议:https://stackoverflow.com/a/20638886/3696204Proper use of generics in abstract java class?

public class ND4JDenseMatrix extends BaseMatrix{

    private INDArray data;

    public ND4JDenseMatrix(int rows, int cols) {
        super(rows, cols);
        this.data = Nd4j.zeros(this.shape);
    }

    // ** Here is my attempt at implementing the abstract method. **
    @Override
    public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
        INDArray product = this.data.mmul(other.data);
        ND4JDenseMatrix result =  new ND4JDenseMatrix(this.nrows, this.ncols);
        result.setData(product);
        return this;
    }
}

这会导致@Override 处出现警告:

Method does not override method from its superclass

但是,如果我在子类中使用相同的方法签名:

public class ND4JDenseMatrix extends BaseMatrix{
    public BaseMatrix mmul(BaseMatrix other) { ...implementation...}
}

然后有人可以传递BaseMatrixmmul() 的任何子级,这几乎肯定会中断。​​

我的问题

鉴于我知道我正在重载而不是覆盖,我如何才能实现我的问题第一部分中描述的强制将类型传递给子类方法的功能?

【问题讨论】:

  • A ND4JDenseMatrix is a BaseMatrix,因此您可以将方法签名保留为public BaseMatrix mmul(BaseMatrix),仍然传入并返回ND4JDenseMatrix
  • @azurefrog 但是这不会允许用户传入BaseMatrix 类的任何孩子吗?因为如果发生这种情况,结果可能会失败......

标签: java inheritance abstract-class


【解决方案1】:

假设 subject 的继承是技术性的,并且不考虑多态性(因为返回值不是 BaseMatrix 类型的多态性),则可以使用泛型。

public abstract class BaseMatrix<T extends BaseMatrix<T>> {
    protected int[] shape;
    protected int nrows;
    protected int ncols;

    public BaseMatrix(int rows, int cols){
        this.nrows = rows;
        this.ncols = cols;
        this.shape = new int[]{nrows, ncols};
    }

    public abstract T mmul(T other);
}

public class ND4JDenseMatrix extends BaseMatrix<ND4JDenseMatrix> {

    private INDArray data;

    public ND4JDenseMatrix(int rows, int cols) {
        super(rows, cols);
        this.data = Nd4j.zeros(this.shape);
    }

    @Override
    public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {
        INDArray product = this.data.mmul(other.data);
        ND4JDenseMatrix result =  new ND4JDenseMatrix(this.nrows, this.ncols);
        result.setData(product);
        return this;
    }
}

【讨论】:

  • @GhostCat 我明白你在说什么,你可能是对的。但这比您在回答中提出的问题更糟吗?看来我必须选择一个问题并忍受它......
  • @GhostCat 确实,我认为这里的继承是技术继承(代码重用)而不是真正的多态性。我会把它添加到答案中
  • 是的,代码重用是游戏的名称。谢谢!
【解决方案2】:

这里:

@Override
public ND4JDenseMatrix mmul(ND4JDenseMatrix other) {

Java 覆盖对于return types协变体

这意味着:

  • 您可以缩小返回类型(您在此处执行的操作:ND4JDenseMatrix 比 BaseMatrix 更具体)
  • 但您不能缩小参数!

这就是您在此处所做的:您将方法限制为比基类中的参数类型特定的参数类型。

换句话说,它只能这样工作:

@Override
public ND4JDenseMatrix mmul(BaseMatrix other) {

这是真正的问题。您会看到,该方法的 调用者 被告知它可以传入 anything 即 BaseMatrix。但是您想将其限制为 BaseMatrix 的更具体的子类型。

【讨论】:

    猜你喜欢
    • 2014-06-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-19
    • 2018-12-13
    • 2019-08-14
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    相关资源
    最近更新 更多