7

I have a feeling this is impossible, but if not it would be very useful.

I’m trying to extend a parent class in a way that the child class only has new methods, no new constructors, no new fields. So the underlying data structure of the child class is identical to the parent class. This tends to occur when I want to give added functions to a built in java class (e.g. Vector3d). Given that the underlying data is identical is it possible in any way to downcast an object initialized as the parent class to the child class so I can use the added functionality. As an example of what I mean see below

import javax.vecmath.Vector3d;

public class Vector3dPlus extends Vector3d{
    //same fields, same constructors as Vector3d

    public double extraMethod(){
        return x+y+z;
    }
}

Try to use the new method added to Vector3d

import javax.vecmath.Vector3d;     

public class Test {

    public static void main(String[] args) {
        Vector3d basic=new Vector3d(1,2,3);

        useExtraMethod(basic); //this line correctly raises an exception, but is there a way around that
    }

    public static void useExtraMethod(Vector3dPlus plus){
        System.out.println(plus.extraMethod());
    }
}

Clearly java gets upset with this because usually we can't guarantee that Vector3dPlus methods will work with all Vector3d. But is there anyway I can say to java that the underlying data structures are the same and so allow all downcasting from all Vector3d to Vector3dPlus.

The current way I deal with this is to put all the extra methods in a generic utilities class, but that's obviously a bit horrible

Roman C
  • 49,761
  • 33
  • 66
  • 176
Richard Tingle
  • 16,906
  • 5
  • 52
  • 77
  • Is there a reason for creating `Vector3d` instead of `Vector3dPlus` right away? Otherwise: `Vector3dPlus { super(int a, int b, int c); }` – Menno Apr 29 '13 at 09:05
  • 2
    I would use an interface – ed_me Apr 29 '13 at 09:05
  • 1
    The reason for not initialising as Vector3dPlus in the first place is that I'm writing a library and I'd like it to use conventional classes externally but be able to use the added functions internally. – Richard Tingle Apr 29 '13 at 09:06
  • I had considered a constructor that copied over the data manually which I will do if necessary. It’s just that that will cause the original Vector3d to need to be garbage collected. – Richard Tingle Apr 29 '13 at 09:08
  • 1
    AFAIK, sun did this with Graphics and Graphics2D? – Simon Hellinger Apr 29 '13 at 09:39
  • You might be interested in using Kotlin, which allows defining "extension methods". Kotlin is very compatible with Java. https://kotlinlang.org/docs/reference/extensions.html – JB Nizet Mar 16 '16 at 19:23

4 Answers4

4

You can achieve it with method overloading and a copy constructor:

public class Vector3dPlus extends Vector3d {
    public Vector3dPlus(Vector3d vector) {
        super( ... ); // parameters from vector
    }

    // rest of your Vector3dPlus code
}

and in your test class, overload the method:

    public class Test {

        public static void useExtraMethod(Vector3d vector) {
            useExtraMethod(new Vector3dPlus(vector));
        }

        public static void useExtraMethod(Vector3dPlus plus){
             System.out.println(plus.extraMethod());
        }

        public static void main(String[] args) {
           Vector3d basic=new Vector3d(1,2,3);
           useExtraMethod(basic); // this line works now
        }
    }
jlordo
  • 37,490
  • 6
  • 58
  • 83
  • I was hoping to avoid the extra garbage collect associated with a copy constructor, but I had a feeling it'd be the way I'd have to do it – Richard Tingle Apr 29 '13 at 09:27
  • @Richtea: In Java you can't cast `BaseClass` objects to `SubClass`. Also, don't assume that garbage collection will be the bottleneck unless you run a profiler and find out that it really is. – jlordo Apr 29 '13 at 09:30
  • Fair enough, it is a bit of a special case, if I had control of the BaseClass there'd be no reason to do it. – Richard Tingle Apr 29 '13 at 09:34
  • +1 I would say it's quite a nice solution, anyway much better than type checking. – dcernahoschi Apr 29 '13 at 10:05
2

I wouldn't focus on the underlying data structure of the class hierarchy but more on the external interface that is exposed.

In your example the Vector3DPlus class has an additional method not found on Vector3D. This differentiates its interface from the base class making them not perfectly substitutable. The compiler correctly raises an exception when the base type is passed to a method expecting the sub type.

I don't believe you can avoid some form of type checking to achieve your desired behavior. Somehow you need to ensure that only instances of Vector3DPlus are passed to your method. That is the price of downcasting.

public static void main(String[] args) {
       Vector3d basic=new Vector3d(1,2,3);

       if(basic instanceof Vector3dPlus){
       useExtraMethod(basic); //this line correctly raises an exception, but is there a way around that
       }
}
Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
0

How about this?

import javax.vecmath.Vector3d;

public class Vector3dPlus extends Vector3d{
    Vector3dPlus {
        super(int a, int b, int c);
    }

    public double extraMethod(){
        return x+y+z;
    }
}

and use like this:

import javax.vecmath.Vector3d;

public class Test {

    public static void main(String[] args) {
       Vector3d basic=new Vector3d(1,2,3);
       Vector3dPlus plus=new Vector3dPlus(1,2,3);

       useExtraMethod(basic);
       useExtraMethod(plus);
    }

    public static void useExtraMethod(Vector3d vector){
        if (vector instanceof Vector3dPlus) {
            System.out.println(((Vector3dPlus) vector).extraMethod());
        }
    }
}
vikingsteve
  • 38,481
  • 23
  • 112
  • 156
0

You can achieve it as explained below [there is no other way to achieve this if you dont want to use instanceof that is]:

public class Test {

    public static void main(String[] args) {
        Vector3dPlus basicPlus = new Vector3dPlus(1,2,3);
        Vector3d basic=basicPlus;

        useExtraMethod(basic); 
    }

    public static void useExtraMethod(Vector3dPlus plus){
        System.out.println(plus.extraMethod());
    }
}

Java has internal mechanism to check type casting. You can't bypass the basic fundamental of "child class is of type parent class" but "Parent class is not of type child class"

Lokesh
  • 7,810
  • 6
  • 48
  • 78