Recently I felt the need to write a Mutable<T>
class so that I can pass immutable objects through methods, whose value can then be changed. Some examples of immutable classes (including primitive types):
int
long
short
byte
double
float
String
(though already has aStringBuilder
classBigInteger
and others.
There is a lot of repetition in the classes, especially the primitive number types. I am sure there is a way around the repetition, but I couldn't figure out how.
Mutable.java
It's quite simple, right?
public class Mutable<T> {
protected T value;
public Mutable(T value) {
setValue(value);
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
I also added some extra primitive number support, so that:
Mutable<Integer> i = new Mutable<>(10);
i.setValue(i.getValue() + 10);
becomes:
MutableInteger i = new MutableInteger(10);
i.add(10);
The interface:
MutableNumber.java
public interface MutableNumber<T extends Number> {
public abstract void add(T number);
public abstract T addAndGet(T number);
public abstract void sustract(T number);
public abstract T sustractAndGet(T number);
public abstract void multiply(T number);
public abstract T multiplyAndGet(T number);
public abstract void divide(T number);
public abstract T divideAndGet(T number);
}
Now the repetitive part:
MutableInteger.java
public class MutableInteger extends Mutable<Integer> implements
MutableNumber<Integer> {
public MutableInteger(Integer value) {
super(value);
}
public void increment() {
value++;
}
public int incrementAndGet() {
return ++value;
}
public void decrement() {
value--;
}
public int decrementAndGet() {
return --value;
}
@Override
public void add(Integer number) {
value += number;
}
@Override
public Integer addAndGet(Integer number) {
value += number;
return value;
}
@Override
public void sustract(Integer number) {
value -= number;
}
@Override
public Integer sustractAndGet(Integer number) {
value -= number;
return value;
}
@Override
public void multiply(Integer number) {
value *= number;
}
@Override
public Integer multiplyAndGet(Integer number) {
value *= number;
return value;
}
@Override
public void divide(Integer number) {
value /= number;
}
@Override
public Integer divideAndGet(Integer number) {
value /= number;
return value;
}
}
MutableLong.java
public class MutableLong extends Mutable<Long> implements MutableNumber<Long> {
public MutableLong(Long value) {
super(value);
}
@Override
public void add(Long number) {
value += number;
}
@Override
public Long addAndGet(Long number) {
value += number;
return value;
}
@Override
public void sustract(Long number) {
value -= number;
}
@Override
public Long sustractAndGet(Long number) {
value -= number;
return value;
}
@Override
public void multiply(Long number) {
value *= number;
}
@Override
public Long multiplyAndGet(Long number) {
value *= number;
return value;
}
@Override
public void divide(Long number) {
value /= number;
}
@Override
public Long divideAndGet(Long number) {
value /= number;
return value;
}
}
MutableDouble.java
public class MutableDouble extends Mutable<Double> implements
MutableNumber<Double> {
public MutableDouble(Double value) {
super(value);
}
@Override
public void add(Double number) {
value += number;
}
@Override
public Double addAndGet(Double number) {
value += number;
return value;
}
@Override
public void sustract(Double number) {
value -= number;
}
@Override
public Double sustractAndGet(Double number) {
value -= number;
return value;
}
@Override
public void multiply(Double number) {
value *= number;
}
@Override
public Double multiplyAndGet(Double number) {
value *= number;
return value;
}
@Override
public void divide(Double number) {
value /= number;
}
@Override
public Double divideAndGet(Double number) {
value /= number;
return value;
}
}
MutableFloat.java
public class MutableFloat extends Mutable<Float> implements
MutableNumber<Float> {
public MutableFloat(Float value) {
super(value);
}
@Override
public void add(Float number) {
value += number;
}
@Override
public Float addAndGet(Float number) {
value += number;
return value;
}
@Override
public void sustract(Float number) {
value -= number;
}
@Override
public Float sustractAndGet(Float number) {
value -= number;
return value;
}
@Override
public void multiply(Float number) {
value *= number;
}
@Override
public Float multiplyAndGet(Float number) {
value *= number;
return value;
}
@Override
public void divide(Float number) {
value /= number;
}
@Override
public Float divideAndGet(Float number) {
value /= number;
return value;
}
}
Concerns:
- Is there a way to remove all the repetition?
- Is my class structure good?
And as usual, anything else is welcome.
2 Answers 2
Your code is a great example of when to stick with primitives ;-) Seriously, though, what you have there is a problem related to primitive arithmetic on undefined types. Operators like +
and -
are primitive-based (except for string concatenation) and as a result they are strongly linked to specific primitives. The code is repeated because each implementation has a different combination of primitives.
In other languages with primitive types, like C, or C++ you would have the same sorts of problems, except you would have unsigned and signed variants as well, or otherwise suffer with large amounts of casting.
In fairness, templates in C++ will help reduce the boiler-plate, but the language itself will generate different instances of the code for you.
So, what's the solution? Well, the best solution is to not encapsulate primitive operations. If you need to do 1 + 1
, then do that, don't do MutableInteger sum = new MutableInteger(1).add(1);
.
Alternatively, to reduce boilerplate, select a single datatype that is a superset of the other types, and use that. In this case, probably BigDecimal
..... hmmm, BigDecimal
is a Number
, but will cause your code to fail....
With BigDecimal
, you can implement all your major methods using BigDecimal
arithmetic, and then have a single protected method to translate it back to the underlying class.... (and throw an exception if the back-cast is not possible?)
public abstract Mutable<T extends Number> {
protected abstract T backCast(BigDecimal result);
protected abstract BigDecimal getDecimal();
public T add(T number) {
return backCast(getDecimal().add(BigDecimal.valueOf(number.toString()));
}
.....
}
Recently I felt the need to write a Mutable class so that I can pass immutable objects through methods, whose value can then be changed. Some examples of immutable classes (including primitive types)
Your language is confusing. What you mean to say, I think, is that you are feeling the need to write a Handle<T>
class so that you can value objects
to methods, and be able to observe the changes to those values in the caller.
(Loose heuristic for identifying value objects: performing an operation on the object gives you a new object instance.)
public interface MutableNumber<T extends Number> {
public abstract T addAndGet(T number);
public abstract T sustractAndGet(T number);
public abstract T multiplyAndGet(T number);
public abstract T divideAndGet(T number);
}
I'm pretty sure Bertrand Meyer would tell you that's a code smell there; your method names are advertising that the methods change the state of the object and return a value. It's not necessarily wrong -- but it's a hint that you should be re-examining whether you have the right solution; which sometimes means re-examining whether you have the right problem
public Float sustractAndGet(Float number) {
value -= number;
return value;
}
If you are going to have that method, it really should do what it says on the tin:
public Float sustractAndGet(Float number) {
subtract(number);
return get();
}
But going back to the top
I felt the need to write a Mutable class
You probably shouldn't have -- it strongly suggests that the code you intend to replace is currently badly written. Objects should only very rarely be responsible for both carrying out calculations and coordinating the calculations. Instead, you should pass the first object to the second. This is the core of the Builder pattern -- the Director coordinates what should happen, and the Builder tracks the calculation.
So you might have a Builder
interface that looks like:
public interface NumberBuilder<T extends Number> {
void add(T number);
void sustract(T number);
void multiply(T number);
void divide(T number);
}
But it's more likely that the interface itself should be more expressive of what you are really trying to achieve:
public interface AccountBuilder<T extends Number> {
void credit(T amount);
void debit(T amount);
void accrueInterest(T rate);
// ...
}
Number
-based class. \$\endgroup\$Mutable<T>
class so that I can pass immutable objects"... does not compute? \$\endgroup\$incrementAndGet
doesn't that make theMutable<T>
(partially) immutable? Why not justincrement
and then change the internal state? Other than that, I have never felt the need for making value types mutable. Ask yourself again, what is the real problem you are trying to solve? \$\endgroup\$