I have a class with code like the following, where I want it to be trivial to use any class/type which represents a number. I find myself defining a large amount of methods, like the following:
public class Range {
private BigDecimal inferior = new BigDecimal(0);
private BigDecimal superior = new BigDecimal(1);
public Range(BigDecimal inferior, BigDecimal superior) {
if (inferior.compareTo(superior) == -1) {
this.inferior = inferior;
this.superior = superior;
}
}
public Range(int inferior, int superior) {
this(new BigDecimal(inferior), new BigDecimal(superior));
}
public Range(Integer inferior, Integer superior) {
this(new BigDecimal(inferior), new BigDecimal(superior));
}
public Range(float inferior, float superior) {
this(new BigDecimal(inferior), new BigDecimal(superior));
}
public Range(double inferior, double superior) {
this(new BigDecimal(inferior), new BigDecimal(superior));
}
}
I haven't even written every combination possible! For instance, one which takes a float and a double, or an int and a BigDecimal.
How could this be achieved in a clean way, so that there are parameters valid for multiple classes/data types which are already predefined, or even primitives? I've considered adapters and proxies, but I regularly find myself not understanding the explanations and I can't figure out if they fit my use case and if so how - this question may have already been answered on SO, but if so at least I would like to see if anyone can explain it to me according to this particular example.
The eight primitive data types supported by the Java programming language are: byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive).
Primitive parameters are parameters of a function that are of primitive types. This means in a function with parameters, any parameter that has type number, string, or boolean is a primitive parameter. When you call a function with parameters, you supply it with arguments.
Use the Builder Pattern. Create a nested static class that accepts each of the distinguishing datatypes for each of the two numbers. Primitive types from byte
through long
will be widened to long
, and float
to double
. BigInteger
s can be converted to BigDecimal
s, and BigDecimal
references will be copied.
public static class Builder {
BigDecimal first;
BigDecimal second;
public void setFirst(long value) { first = new BigDecimal(value); }
public void setFirst(double value) { first = new BigDecimal(value); }
public void setFirst(BigInteger value) { first = new BigDecimal(value); }
public void setFirst(BigDecimal value) { first = value; }
public void setSecond(long value) { second = new BigDecimal(value); }
public void setSecond(double value) { second = new BigDecimal(value); }
public void setSecond(BigInteger value) { second = new BigDecimal(value); }
public void setSecond(BigDecimal value) { second = value; }
public Range build() {
if (first == null || second == null) {
throw new IllegalArgumentException("Must supply both values.");
}
return new Range(first, second);
}
}
The Builder pattern allows validation before building the desired object, and it bypasses the "constructor explosion" that would occur attempting to cover every possible combination. With n possible types, you have 2 * n builder setter methods instead of n2 constructors.
I included long
, even though it can be widened to double
legally, for precision reasons, because there are very high values of type long
that can't be precisely represented as double
s.
Then, your constructor becomes:
public Range(BigDecimal first, BigDecimal second) {
if (first.compareTo(second) < 0) {
this.inferior = first;
this.superior = second;
}
else {
this.inferior = second;
this.superior = first;
}
}
I changed the == -1
to < 0
to match the compareTo
contract, and added the else
case that switches them if needed.
Use class Number
:
public Range(Number inferior, Number superior)
Integer, Long, Double
- all of them are subclasses of Number
.
Alternatively, use generics:
public class Range<T> {
private T inferior;
private T superior;
public Range(T inferior, T superior) {
this.inferior = inferior;
this.superior = superior;
}
}
Usage:
Range<Long> rangeLong = new Range<>(0L, 1000000000L);
Range<Double> rangeDouble= new Range<>(0d, 457.129d);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With