简体   繁体   中英

Java let superclass return type of extended class

My current situation: I have a superclass Matrix:

public class Matrix {
  private final float[] values;
  private final int numberOfRows;
  private final int numberOfColumns;

  public Matrix(int rows, int columns, float... values) {
    this.values = new float[numberOfRows * numberOfColumns];
    this.numberOfRows = numberOfRows;
    this.numberOfColumns = numberOfColumns;
    System.arraycopy(values, 0, this.values, 0, values.length);
  }

  public Matrix scale(int scaleFactor) {
    //Some code that returns a scaled version of the matrix
  }
}

which is extended by the Vector class that has some vector-specific methods (like eg calculating the length of the vector).

Now, let's say I have a vector object named, and I want to scale it by some factor. Then I would call vector.scale(1234); which results in a matrix object.

However, I would expect the result to be a vector object. I could of course override the scale method in the Vector class and cast the superclass's result to an object of the Vector class, but this doesn't seem like the proper way to do this.

Could anyone give me some pointers on how to implement this so that I can extend the Matrix class and have objects of the subclasses return their own types of objects when they call the scale method?

If Vector extends Matrix , you can:

public class Matrix {
    public Matrix scale(int scaleFactor) {
        ... return Matrix ...
    }
}

public class Vector extends Matrix {
    ...

    @Override
    public Vector scale(int scaleFactor) { // <-- return sub type here!
        ... return Vector ...
    }
}

The rule that overridden methods must have the same type has been weakened in Java 5: You can now use subtypes as well to make overridden methods "more specific."

The advantage of this approach over generics is that it's clean, no (implicit) casting is required and it exactly limits the types without any generics magic.

[EDIT] Now Vector is just a special kind of Matrix (the 1-column/row type) which means that the code in scale() is the same.

Unfortunately, this doesn't work:

public class Vector extends Matrix {
    ...

    @Override
    public Vector scale(int scaleFactor) { // <-- return sub type here!
        return (Vector) super.scale(scaleFactor);
    }
}

since you can't cast Matrix instances into a Vector . The solution is to move the scale code into a helper method:

public class Matrix {
    protected void doScale(int scaleFactor) {
        ... original scale() code here...
    }

    public Matrix scale(int scaleFactor) {
        doScale(scaleFactor);
        return this;
    }
}

public class Vector extends Matrix {
    ...

    @Override

    public Vector scale(int scaleFactor) {
        doScale(scaleFactor);
        return this;
    }
}

If the class is immutable (and it probably should be), then you need to use the copy constructor, of course:

    public Matrix scale(int scaleFactor) {
        Matrix result = new Matrix(this);
        result.doScale(scaleFactor);
        return result;
    }

You could make scale generic. Like

public class Matrix<T> {
  // ...
  public T scale(int scaleFactor) {
    // Some code that returns a scaled version of the matrix
  }
}

then your sub-classes could extend Matrix<Vector> . Note that it would be difficult to practically implement scale in Matrix ; it might be better to make it abstract like

public abstract class Matrix<T> {
  // ...
  public abstract T scale(int scaleFactor);
}

Then your sub-classes must provide an implementation.

An easy thing you can do is to make the method generic:

public <T extends Matrix> T scale(int scaleFactor) { ... }

When using it, you need to provide the generic type. For example, if you want a Vector to be returned, you have to do:

Matrix something = ....
Vector result = something.<Vector>scale(...);

Even better option is to make the scale method abstract :

public <T extends Matrix> T scale(int scaleFactor);

Then, in all subclasses of Matrix , you'd be forced to implement the method with a return-type that is a sub-type of Matrix .

您正在达到Java泛型类型的局限性之一,如果不在子类中重新定义public Matrix scale(int scaleFactor) ,然后重新定义返回类型,我看不到一种简单的解决方法: public Vector scale(int scaleFactor)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM