web-dev-qa-db-ja.com

Java:サブクラスの継承されたメソッドの戻り値の型を、サブクラスと同じにする方法を教えてください。

私はJavaに不慣れで、作成したクラスのサブクラスを形成するのに問題があります。私が作成したクラスは、_Vector2D_と呼ばれ、_Vector2D_オブジェクトを引数として取り、_Vector2D_オブジェクトを返すadd(Vector2D addend)などのメソッドを含みます。このメソッドは特に、2つのベクトルを加算して合計を返すように設計されています。

次に、_Position2D_というサブクラスのコーディングを開始しました。これは、追加の関数を除き、_Vector2D_クラスのすべての機能を持ち、内部で_Vector2D_を使用する予定です。引数、戻り値の型など、既存のクラスを_Position2D_で置き換えて、add(Position2D addend)関数が2つの_Position2D_ベクトルを加算し、合計をaとして返すようにします。 _Position2D_。

_Position2D_が_Vector2D_から継承されると、このような関数は_Vector2D_オブジェクトに関して機能し続け、_Position2D_オブジェクトが渡されるとすぐにエラーを吐き出すという問題が発生しますそれを通して。

_Vector2D_クラスの関連コードは次のとおりです。

_public class Vector2D{

    //Field Variables
    private double x; //The vector's x-coordinate
    private double y; //The vector's y-coordinate

    //Constructors
    public Vector2D(){
        //Constructs empty vector.
        setX(0);
        setY(0);
    }

    //Methods
    public void setX(double newX){
        //This method is not problematic when inherited for the subclass.
        x = newX;
    }

    public void setY(double newY){
        //This method is not problematic too.
        y = newY;
    }

    public Vector2D add(Vector2D addend){
        //Adds two vectors and returns the sum.
        //This method, however, does pose a problem.
        Vector2D sum = new Vector2D();
        sum.x = x + addend.x;
        sum.y = y + addend.y;
        return sum;
    }

}
_

_Position2D_のadd(Position2D addend)のコードが何であるかについて私が現在考えることができる唯一の回避策は:

_public Position2D add(Position2D addend){
    //Adds two position vectors and returns the sum.
    Position2D sum = new Position2D();
    sum.x = x + addend.x;
    sum.y = y + addend.y;
    return sum;
}
_

つまり、_Vector2D_からメソッドを繰り返し、手動で変更します。もちろん、これは効率的ではなく、時間を浪費します。特に、同様のメソッドに対して同じことを行う必要がある場合はいつでも、さらにサブクラスを作成するつもりの場合は常にそうです。 _Velocity2D_。したがって、これを行うためのよりクリーンで効率的な方法を探しています。

5
Involutius

継承は「ある」種類の関係を定義することを忘れないでください。コード

class Position2D extends Vector2D

すべてのPosition2DはVector2Dですが、その逆ではないことをコンパイラーに伝えます。

上記の宣言をすでに行っていると想定して、次の例を確認してください。

// obviously okay, type of instance matches reference type
Vector2D vecVecRef = new Vector2D();
Position2D posPosRef = new Position2D();
// this is okay because a Position2D is a Vector2D
Vector2D posVecRef = new Position2D();
// The compiler will not automatically convert a Vector2D to a Position2D
Position2D vecPosRef = new Vector2D(); //Compiler error!

Position2D変数がVector2Dオブジェクトを参照することはありません。型キャストを試みると、コンパイラーは「問題ありません...私はあなたを信頼しています」と言いますが、JVMは、Vector2DをPosition2Dに割り当てようとしていることを実際に検出すると怒ります。実行時にClassCastExceptionが発生します。

// compiles fine but raises ClassCastException in runtime
Position2D vecPosRef = (Position2D) new Vector2D();

これは、サブクラスをスーパークラスにキャストできるのはクラスのみであり、その逆はできないためです。したがって、基本的に、Vector2DをPosition2Dにキャストすることはできず、キャストせずに割り当てることもできません。

この問題の最も簡単な解決策は、特定のVector2DオブジェクトからPosition2Dオブジェクトを作成するコンストラクターをサブクラスで定義することです。

class Position2D extends Vector2D {
    Position2D() {
        // default stuff
    }

    Position2D(Vector2D v) {
        // you currently don't have the getX and getY methods
        // so define them in your superclass
        setX(v.getX());
        setY(v.getY());
    }
}

その1つのシンプルで便利なコンストラクターを使用すると、次のようなコードを使用できます。

public class Inheritance {
    public static void main(String[] args) {
        Position2D pos1 = new Position2D();
        Position2D pos2 = new Position2D();
        pos1.setX(3);
        pos1.setY(4);
        pos2.setX(5);
        pos2.setY(6);
        Position2D pos3 = new Position2D(pos1.add(pos2)); // using constructor
        System.out.println(pos3.getX()); // this prints 8.0
    }
}

ご覧のとおり、この方法は、すべてのサブクラスメソッドを書き換えるよりもはるかに拡張可能です。

3
Shashank Gupta

依存性逆転原理factory methodパターン

戻り値の型は、_Vector2D_と_Position2D_の両方のスーパータイプである必要があります。

それを_IVector2D_(インターフェース)と呼びましょう。

解決策は次のとおりです。

_public interface IVector2D {

    public void setX(double newX);  
    public void setY(double newY);  
    public double getX();   
    public double getY();       
    public IVector2D add(IVector2D addend);
    public IVector2D getInstance(); // this helps decouple the instantiation

}
_

その後

_public class Vector2D implements IVector2D {

    private double x; //The vector's x-coordinate
    private double y; //The vector's y-coordinate

    public Vector2D(){ setX(0); setY(0); }

    public IVector2D getInstance(){ return new Vector2D(); }

    @Override
    public void setX(double newX) { x = newX; }

    @Override
    public void setY(double newY) { y = newY; }

    @Override
    public double getX() { return this.x; }

    @Override
    public double getY() { return this.y; }

    @Override
    public IVector2D add(IVector2D addend) {
        IVector2D sum = getInstance();
        sum.setX(this.getX()+addend.getX());
        sum.setY(this.getX()+addend.getY());
        return sum;
    }
}
_

拡張するときは、getInstance()をオーバーライドしてコンストラクタを実装します。

_public class Position2D extends Vector2D {      
    public Position2D(){ super(); }     
    @Override
    public IVector2D getInstance(){ return new Position2D(); }
}
_

getInstance()add()をオーバーライドしているため、実際には_Position2D_型の参照内で_Ivector2D_型のオブジェクトを返していることに注意してください。 _Position2D_のgetInstance()を呼び出します。その抽象化により、add()メソッドがシームレスに機能できるようになりました。

テストプログラムは次のようになります。

_public static void main(String[] args) {
    IVector2D a = new Position2D();
    IVector2D b = new Position2D();
    a.setX(1.2d);
    a.setY(2.3d);
    b.setX(0.33d);
    b.setY(9.0d);
    IVector2D c = a.add(b);
    System.out.println(c.getX());
}
_

出力:

_1.53
_
4