5

I am rather inexperience in Java, and I'm having a problem in forming a subclass of a class I have created. The class I have made, called Vector2D, contains methods, such as add(Vector2D addend), that takes a Vector2D object as an argument, and returns a Vector2D object. This method in particular is designed to add two vectors, and return the sum.

Then, I started coding a subclass, called Position2D, which I intend to have all of the functionality of the Vector2D class, except have additional functions, and have any usage of Vector2D inside the preexisting classes, be it as an argument, return type, etc., replaced with Position2D, so that the add(Position2D addend) function adds two Position2D vectors together, and returns the sum as a Position2D.

The issue arises that, when Position2D inherits from Vector2D, such functions continue to work in terms of Vector2D objects, spitting out an error as soon as a Position2D object is passed through it.

The relevant code for the Vector2D class is as follows:

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;
    }

}

The only work-around I can currently think of as to what the code in add(Position2D addend) in Position2D could be is:

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;
}

i.e., repeat the method from Vector2D, and manually modify it. This, of course, is not efficient and wastes time, especially so whenever the same has to be done for similar methods, and more so whenever I intend to make more subclass, e.g. Velocity2D. Therefore, I am looking for a much cleaner and efficient means of doing this.

  • I think the problem here is that a point is not a vector. (Points and vectors are also not mutable but that's a different issue.) – Doval Aug 27 '14 at 19:42
  • 1
    I don't think that's the issue at hand: I've defined both the "Vector" and "Position" class from scratch, and those are just names for the classes. I could have renamed them VectorA and VectorB, and the same issue arises. – Involutius Aug 27 '14 at 19:50
  • 1
    My point was that when things like this become an issue, it's usually because inheritance is being used for things that are not substitutes for the superclass. Yes, the same issue arises if you relate two arbitrary classes through inheritance, but that raises the question of why you're relating two arbitrary classes through inheritance. – Doval Aug 27 '14 at 19:54
  • I see what you mean, but these classes don't seem arbitrary to me. I have defined a general vector class, and from that, I have intended to extend the functionality to more specific vectors, in this case, a position vector. From my understanding, a position vector should be a subclass of a vector: it is still a vector. Perhaps my use of the term "position" instead of "position vector" could have caused confusion? – Involutius Aug 27 '14 at 20:04
  • Adding points doesn't make sense as explained in the link; only adding displacement vectors makes sense. So if you mean to have points and vectors, the inheritance relationship doesn't hold because add isn't defined for points. If you have position vectors as you claim you do, then you already have those in the form of Vector2D; it makes no sense to create a separate type. If your intention is to distinguish between vectors with different units then you should probably be using generics so you can't add Vector2D<Kilometers> with Vector2D<KilometersPerHour>. – Doval Aug 28 '14 at 0:21
3

Remember inheritance defines a "is a" kind of relationship. The code

class Position2D extends Vector2D

tells the compiler that every Position2D is a Vector2D, but not vice versa.

Check out the following examples assuming that you already made the above declaration:

// 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!

A Position2D variable can never refer to a Vector2D object. If you try typecasting it, the compiler will say "fine...I trust you" but then the JVM will get angry when it actually finds out that you are trying to assign a Vector2D to a Position2D. It will raise a ClassCastException at runtime.

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

This is because you can only Class cast a subclass into a superclass and not vice versa. So basically, you cannot cast Vector2D to Position2D and you cannot assign it without casting either.

The simplest solution to this problem is to have a constructor defined in your subclass that makes a Position2D object out of a given Vector2D object.

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());
    }
}

With that one simple and convenient constructor, you can use code like this:

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
    }
}

As you can see, this way is much more extensible than rewriting all of the subclass methods.

  • 1
    The super operator refers to the sub class of the base class. So, if you want to call setX or setY from the base class, then you would say super.setX and super.setY. It's a good idea to initialize the base class in the constructor of the sub class. You can do that by saying super(arg1, arg2, arg3, ...) – Cameron McKay Aug 27 '14 at 20:53
  • 1
    In this case, calling the super constructor is redundant because Vector2D only has a default constructor and that constructor is implicitly called in every Position2D constructor anyways. Also, setX and setY haven't been overridden, so using super.setX and super.setY is also redundant. But yes, in the case of a constructor that uses parameters that are relevant to the superclass, you should use the super keyword to access the base class constructor with those arguments. – Shashank Gupta Aug 27 '14 at 21:26
  • The code works. In addition, looking at the line in your post (line 9 of Inheritance class): Position2D pos3 = new Position2D(pos1.add(pos2));, is it possible/good practice to essentially reduce that to Position2D pos3 = pos1.add(pos2) by adding the = new Position2D(..) portion of (line 9 of Inheritance class) into the add(..) function inside the Position2D class? – Involutius Aug 27 '14 at 22:23
  • @EternalCode It's certainly possible if you wish to avoid using that syntax, but in general, excessive overriding is bad practice. You already wrote an implementation of add that can do that in your opening post. But I was under the impression that you wanted to avoid doing stuff like that. If you really want to avoid it altogether, it's probably better practice to use interfaces as described in user61852's answer. Using an interface like IVector2D, a variable of type IVector2D can refer to any class that implements its methods. – Shashank Gupta Aug 27 '14 at 23:26
  • I see a problem with that solution. It assumes there is no additional state in the Position2D object. If for example Position2D had an additional flag to specify relative as compared to absolute position, that flag will get lost. And the user shouldn't have to know that the add() method belongs to a superclass and the result needs to be upgraded. – Florian F Aug 30 '14 at 15:56
3

You can by using the dependency inversion principle and the factory method pattern.

The return type should be a supertype of both Vector2D and Position2D.

Let's call that IVector2D ( an interface ).

The solution would be:

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

}

An then

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;
    }
}

When extending, override getInstance() and implement a constructor:

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

Note that you are actually returning an object of type Position2D, as you wanted, inside a reference of type Ivector2D, because you are overriding getInstance() and add() will call the getInstance() of Position2D. That abstraction allowed for the add() method to work seamlessly.

The test program would be:

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());
}

Ouput:

1.53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.