8

I keep coming back to variants of this problem: it probably has a very simple solution, but I can't seem to figure it out...

I have a bunch of classes of the form xQuantity, e.g. DistanceQuantity, AreaQuantity, etc., which extend a class DimensionQuantity. Now you can add or subtract DistanceQuantity's or AreaQuantity's, etc., but you can't mix them, so I think I need to have (short) add, subtract, etc., methods in the subclasses, but I want to reduce any logic duplication to a minimum. However, I need to return an object of the subclass, and this seems difficult to do from the superclass method. I believe this can be done using reflection, but AFAIK you still need to do a cast at the end in the subclass method, and I am told that reflection can be expensive... The best I have come up with so far is:

In DistanceQuantity (and the other similar ones):

public DistanceQuantity() {     
}

public DistanceQuantity add(DistanceQuantity d1) {
    DistanceQuantity dn = new DistanceQuantity(); 
    Object o = super.add(dn, this, d1, DistanceUnit.REF_UNIT);
    return (DistanceQuantity) o;
}

In DimensionQuantity (minus some less relevant statements):

public Object add(DimensionQuantity dn, DimensionQuantity d1, DimensionQuantity d2, 
  AbstractUnit au) {
    dn.unit = au;
    dn.scalar = d1.scalar + d2.scalar;
    dn.units = dn.scalar;    
    return dn;   
}

Can anyone come up with leaner code - that is still type-safe? TIA

tangens
  • 39,095
  • 19
  • 120
  • 139
Paul Morrison
  • 1,694
  • 3
  • 20
  • 35

3 Answers3

11

You can use Generics like this :

public abstract class DimensionQuantity<T extends DimensionQuantity>{
    public abstract T add(T parameter);
}

and you extends it like this :

public class DistanceQuantity extends DimensionQuantity<DistanceQuantity>{
    public DistanceQuantity add(DistanceQuantity parameter){
        //Whatever
        return null;
    }
}

And for the initial question, it's a really bad idea (and a bad practice) to have a superclass which uses one of its sub-classes.


Resources :

On the same topic :

Community
  • 1
  • 1
Colin Hebert
  • 91,525
  • 15
  • 160
  • 151
  • it's better (clearer once you have lots of generics) to name the generics something closer to T_DimensionQuantity – Egwor Aug 30 '10 at 19:21
  • You forgot the abstract keyword. ;) Just using 'T' is pretty standard practice I think. – Michael Aug 30 '10 at 19:26
  • @mangst thanks, it's abstract now. @Egwor, like @mangst said, T is common. T for Type, in Collection it's E for Element, in Map it's K and V for Key and Value. Usually it's the first letter of your generics meaning. – Colin Hebert Aug 30 '10 at 19:32
  • @Colin Do you consider the design of the `Ordering` class from Guava also bad, because it relies directly on sub classes (`nullsLast`, `reverse`, ...)? Just curious. – whiskeysierra Aug 30 '10 at 20:17
  • @Willi Schönborn Well, to be precise as long as the subclasses aren't part of the public interface of the parent, it's not that bad. And I shouldn't have used "Design flaw" but rather "Bad practice". – Colin Hebert Aug 30 '10 at 20:29
  • @Colin HEBERT and @Benoit Courtine both seem to like generics, but you both apparently missed (and I forgot to point it out) that I want the '+' in the superclass, *not* in the subclass. And @Colin's code returns null, which is exactly the wrong answer! So maybe super/subclassing is not the right approach... – Paul Morrison Sep 01 '10 at 17:23
  • "It returns null"... Are you kidding me ?!? – Colin Hebert Sep 01 '10 at 17:27
  • You want the add() method in the super class ? What does this means ? Give some examples. – Colin Hebert Sep 01 '10 at 17:27
  • @Colin - I am seeing //Whatever return null; } } Aren't you? – Paul Morrison Sep 02 '10 at 00:50
  • I want to put shared logic in a common place - I suspect subclass/superclass is the wrong technique. It would probably be better to use a Helper class. I'll try that... – Paul Morrison Sep 02 '10 at 00:53
  • @Paul Morrison, of course, you have to replace the content of `add()` with your own logic and return your result. And I think that you're trying to solve a conception problem by adding another conception problem. You shouldn't put every possible `add()` variant in a common place, it's really hard to maintain and it isn't scalable at all. What if you create a new subclass of `DimensionQuantity` ? What if another developer which can't access to your helper code wants to create its subclass ? You should really look at this solution first. – Colin Hebert Sep 02 '10 at 05:51
0

In Java 6, you can try something like that. But using generics is also a good idea :

public abstract class DimensionQuantity {

    protected int quantity;

    public DimensionQuantity (int quantity) {
        this.quantity = quantity;
    }

    public abstract DimensionQuantity add(DimensionQuantity dq);
}

public class DistanceQuantity extends DimensionQuantity {

    public DistanceQuantity(Quantity quantity) {
        super(quantity);
    }

    /* 
     * A sub-class as return type is authorized.
     * If dq is not a DistanceQuantity, a ClassCastException is thrown, but you can change this.
     */
    @Override 
    public DistanceQuantity add(DimensionQuantity dq) throws ClassCastException {
        return new DistanceQuantity(this.quantity + (DistanceQuantity) dq.quantity);
    }
}
Benoit Courtine
  • 7,014
  • 31
  • 42
0

Create an enum for the Dimensions:

public enum Dimension { AREA, DISTANCE,...}

Throw away your subclasses. Instead create only DimensionQuantity objects, and force each to have an (immutable) Dimension, set at create time.

Implement addition in DimensionQuantity, first checking that the Dimensions of the two quantities being added are the same.

Voila! Fewer classes, type safety, no duplicate code.

DJClayworth
  • 26,349
  • 9
  • 53
  • 79
  • "first checking that the Dimensions of the two quantities being added are the same. " - that sounds like run-time to me! – Paul Morrison Sep 01 '10 at 17:23