17

I am trying to define a abstract Range class which will serve as the base implementation of a number of range classes. The intended use is irrelevant for this question, but so far I have:

/**
 * Abstract generic utility class for handling ranges
 */
public abstract class Range<T extends Number> {

  // Variables to hold the range configuration
  private T start;
  private T stop;
  private T step;

  /**
   * Constructs a range by defining it's limits and step size.
   *
   * @param start The beginning of the range.
   * @param stop The end of the range.
   * @param step The stepping
   */
  public Range(T start, T stop, T step) {
    this.start = start;
    this.stop = stop;
    this.step = step;
  }
}

Now I want to add a constructor with that only takes start and stop, and sets a default step value of 1, no matter what implementation of Number T is (e.g. if T is Integer [one] would have the value 1, and if T is Long [one] would have the value 1L, and so on). I would like something like:

protected Range(T start, T stop) {
  this(start, stop, [one]);
}

but I can't figure out how so set the value of [one]. Since Java is still fairly new to me, I have tried with:

private static final T one = 1;

which doesn't work because T is obviously defined in the instantiation of Range.this. Also I have tried:

protected static abstract T getOne();

which also doesn't work because T is defined in the instantiation of Range.this plus static and abstract don't work together.

I need some way for extending classes to be forced to define the value of [one] no matter what implementation of Number Range is implemented for.

Ultimately I would also like to set a zero value as default start, such that I get a constructor that looks like this:

protected Range(T stop) {
  this([zero], stop, [one]);
}
beruic
  • 5,517
  • 3
  • 35
  • 59
  • 2
    I'm nto sure Number is the right superclass to use. There is interesting question on this topic: http://stackoverflow.com/questions/2721390/how-to-add-two-java-lang-numbers - I mean even if you figure this out, how would you add the step to start to iterate over the range? – Jan Zyka May 23 '14 at 10:03
  • 1
    `this(start, stop, (T) (new Integer(1)));` – Dmitry Bychenko May 23 '14 at 10:06

3 Answers3

18

One solution would be to ask the subclasses for the default step to use:

public abstract class Range<T extends Number> {

    private T start;
    private T stop;
    private T step;

    public Range(T start, T stop, T step) {
        this.start = start;
        this.stop = stop;
        this.step = step;
    }

    protected Range(T start, T stop) {
        this.start = start;
        this.stop = stop;
        this.step = getDefaultStep();
    }

    protected abstract T getDefaultStep();

}

public class IntegerRange extends Range<Integer> {

    public IntegerRange(Integer start, Integer stop, Integer step) {
        super(start, stop, step);
    }

    public IntegerRange(Integer start, Integer stop) {
        super(start, stop);
    }

    @Override
    protected Integer getDefaultStep() {
        return 1;
    }

}

public class DoubleRange extends Range<Double> {

    public DoubleRange(Double start, Double stop, Double step) {
        super(start, stop, step);
    }

    public DoubleRange(Double start, Double stop) {
        super(start, stop);
    }

    @Override
    protected Double getDefaultStep() {
        return 1d;
    }

}
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • Boom! +1 for the quick answer. – Adam Arold May 23 '14 at 10:06
  • 1
    I second that, although that every implementing class has to provide a default step regardless if it doesn't even use the short constructor. – Smutje May 23 '14 at 10:08
  • You cannot do that. `Range.getDefaultStep` cannot be called before `Range` has been instanciated, and thus you cannot use it in the constructor. – beruic May 23 '14 at 11:15
  • Sorry, I just tried is the way you write it. I used `this(start, stop, getOne());` before which doesn't work. – beruic May 23 '14 at 11:17
  • @beruic Yes, that's why the 2-arg constructor can't call the 3-arg one. You have to explicitly set the members in the 2-arg one too, as I provided. – sp00m May 23 '14 at 11:58
6

You can use this implementation:

protected Range(T start, T stop) {
  this(start, stop, (T) (new Integer(1)));
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

You will run into another problems ... but here is your number 1:

Number ONE = new Number() {
    @Override
    public int intValue() {
        return 1;
    }

    @Override
    public long longValue() {
        return 1L;
    }

    @Override
    public float floatValue() {
        return 1.0f;
    }

    @Override
    public double doubleValue() {
        return 1.0;
    }

    @Override
    public byte byteValue() {
        return 1;
    }

    @Override
    public short shortValue() {
        return 1;
    }
};
Jan Zyka
  • 17,460
  • 16
  • 70
  • 118
  • Care to elaborate on the other problems? :) – beruic May 23 '14 at 13:09
  • It would be interesting to see how you will iterate over the range in generic way, I mean adding the `step` to the `start` and comapre it with `stop`. You can require the subclasses to provide you with `compare(T,T)`, `add(T,T)` etc. but it doesn't look nice IMHO :) On the other hand I don't see better solution ... – Jan Zyka May 23 '14 at 13:27
  • You can solve the comparable problem with Range, the arithmetic operations will be still painful – Jan Zyka May 23 '14 at 13:34
  • Lat comment, I promise :) http://stackoverflow.com/questions/8957990/java-lang-number-doesnt-implement-or-any-other-operators – Jan Zyka May 23 '14 at 13:37