3
\$\begingroup\$

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 a StringBuilder class
  • BigInteger

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:

  1. Is there a way to remove all the repetition?
  2. Is my class structure good?

And as usual, anything else is welcome.

asked Nov 17, 2015 at 23:59
\$\endgroup\$
5
  • \$\begingroup\$ I've edited the question to remove the what-didn't-work part, as 1) it's not exactly part of a good CR question (as you understood correctly) and 2) operator overloading looks like it works for primitive wrapper classes only because of unboxing, so naturally they wouldn't work for any Number-based class. \$\endgroup\$ Commented Nov 18, 2015 at 1:24
  • 1
    \$\begingroup\$ Also, "I felt the need to write a Mutable<T> class so that I can pass immutable objects"... does not compute? \$\endgroup\$ Commented Nov 18, 2015 at 1:25
  • \$\begingroup\$ I get what you mean after reading your problem statement in full (and reading the excellent answers provided by @rolfl and @VoiceOfUnreason). I kind of agree with the latter that your solution should be looking for the right problem... \$\endgroup\$ Commented Nov 18, 2015 at 6:11
  • \$\begingroup\$ Why incrementAndGet doesn't that make the Mutable<T> (partially) immutable? Why not just increment 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\$ Commented Nov 18, 2015 at 7:52
  • \$\begingroup\$ Recently I felt the need to write a... that is what the YAGNI maxim was for. \$\endgroup\$ Commented Nov 18, 2015 at 12:12

2 Answers 2

4
\$\begingroup\$

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()));
 }
 .....
}
answered Nov 18, 2015 at 1:31
\$\endgroup\$
4
\$\begingroup\$

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);
 // ...
}
answered Nov 18, 2015 at 5:05
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.