Frage

I am trying to add some convenience methods to Java's BigDecimal and create a CustomBigDecimal class. Say I want to add a method reciprocal(). I have tried doing this with inheritence as follows:

public class CustomBigDecimal extends BigDecimal
{
   .....
   .....
   public CustomBigDecimal reciprocal()
   {
       .....
   }       
}

CustomBigDecimal foo1 = new CustomBigDecimal(1);
CustomBigDecimal foo2 = new CustomBigDecimal(2);
CustomBigDecimal foo2 = foo1.add(foo2); //cannot cast superclass to subclass

The problem with this approach is that I cannot cast a superclass to subclass (for reasons I am well aware of). And all the methods of the superclass return a BigDecimal. I have thought of a solution to solve this using composition as follows:

public class CustomBigDecimal 
{
    private BigDecimal val;
    CustomBigDecimal(BigDecimal val)
    {
       this.val = val;
    } 
    ......
    ......  
    public CustomBigDecimal add(CustomBigDecimal augend)
    {
        return new CustomBigDecimal(val.add(augend.getBigDecimal()));
    }       
    .....
    .....
    public CustomBigDecimal reciprocal()
    {
        ....
    } 
}

But if I go with the second approach, I have to write every method of BigDecimal.

Is there a better approach to solve this problem?

War es hilfreich?

Lösung

If you aren't actually changing the representation of BigDecimal itself, and are just adding some helper methods, then you can have another class that contains your helper methods statically. ie

class BigDecimalMethods{
     public static BigDecimal reciprocal(BigDecimal bd){
     }
     //etc
}

Andere Tipps

Forget about inheritance. It will force you to handle both CustomBigDecimals and non-custom BigDecimals.

Imagine what you would do if you wanted to add a convenience toProperCase() method to the String class (and this is a very common and frequent issue). You would be forced to create a StringUtils class, because the String class is final. The downside is that then you will need to use StringUtils.toProperCase(someString) instead of someString.toProperCase().

If the String class was not final, you could create a subclass and add the method there. But this would sucks, you still could not do someString.toProperCase(), you would need something like:

if (!(someString instanceof MyStringSubclass)) {
    someString = new MyStringSubclass(someString);
}
String somethingElse = ((MyStringSubclass) someString).toProperCase();

And, that code is indeed horrible. In your CustomBigDecimal class, you hit the same issue:

if (!(someBigDecimal instanceof CustomBigDecimal)) {
    someBigDecimal = new CustomBigDecimal(someBigDecimal);
}
CustomBigDecimal somethingElse = ((CustomBigDecimal) someBigDecimal).reciprocal();

And again, that sort of code sucks.

In ruby, javascript and some other languages, you could mixin some new method into an existing class without having to alter it. This would be the correct solution for the problem. Unfortunately, java does not allows this. So, the better solution is the utils approach:

public class BigDecimalUtils {
    private BigDecimalUtils() {}

    public static BigDecimal reciprocal(BigDecimal a) {
       ...
    }
}

The utils approach is an anti-pattern, but since there is no way to mixin a method into an existing java class, it is the best approach to do. Using a subclass is still a worse anti-pattern. The correct solution would be to add mixins to the java language, but this is of course not an option.

Note: I am not considering options regarding in altering the actual classes in the JDK's rt.jar file, nor to emulate that with bytecode-manipulating classloaders. That would be a serious overkill for this.

You could do something like this:

CustomBigDecimal foo1 = new CustomBigDecimal(1);
CustomBigDecimal foo2 = new CustomBigDecimal(2);
CustomBigDecimal foo3 = new CustomBigDecimal(foo1.add(foo2).intValue());
    // Or floatValue()...

You will either have to implement a helper static method or a constructor like so:

public CustomBigDecimal(BigDecimal bigDecimal) {
    super(bigDecimal.toString());
}

CustomBigDecimal foo2 = new CustomBigDecimal(foo1.add(foo2));

But if I go with this approach, I have to implement every method of BigDecimal.

I don't think you will need to implement every method. Rather probably every constructor:

class CustomBigDecimal extends BigDecimal
{

    public CustomBigDecimal(BigInteger val) {
        super(val);
    }

    public CustomBigDecimal(int i)
    {
        super(String.valueOf(i));
    }

    public CustomBigDecimal(String str)
    {
        super(str);
    }
    public CustomBigDecimal(BigDecimal bigDecimal)
    {
       this(bigDecimal.toString());   
    }

   CustomBigDecimal foo1 = new CustomBigDecimal(1);
   CustomBigDecimal foo2 = new CustomBigDecimal(2);
   CustomBigDecimal foo3 = new CustomBigDecimal(foo1.add(foo2).toString());
   CustomBigDecimal foo4 = new CustomBigDecimal(foo1.add(foo2));

}

Vandale's answer is the best solution. However, if you really wanted to do this, just "override" the methods of BigDecimal that return a BigDecimal

public CustomBigDecimal abs() {
    return new CustomBigDecimal(super.abs());
}

The problems with this are that all of your new CustomBigDecimal-returning methods won't actually override BigDecimal's version, and that you'll need to override every constructor (there are a lot), plus an extra CustomBigDecimal(BigDecimal abs)

You can hopefully see how messy this is, and why I think you should go with Vandale's answer

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top