Modeling number types and arithmetic operations between them in Java

What I am trying to do:

I am creating a class hierarchy that represents and has utilities for many concepts of linear algebra (both practical linear algebra and the Java practice that I am learning right now). Everything went very well until I decided to add different types of numbers, i.e. integers, rational, real and complex (for now).

I want to be able to work with them (for example, inside the Matrix class) without caring about which number I am working with, and implement all the operations inside different classes representing each type of number.

What I thought / tried:

I thought it would be a good idea to create an interface called "AnyNumber" (since Number is already taken over by the Java API), on which I would define all the necessary abstract methods, and then implement this interface for each type of class number. The problem is I can't figure out how to handle the class types.

This is what the interface expression looks like:

public interface AnyNumber {

    public AnyNumber add(AnyNumber other);
    public AnyNumber subtract(AnyNumber other);
    public AnyNumber multiply(AnyNumber other);
    public AnyNumber divide(AnyNumber other);

}

      

This way I will be able to work with any objects that implement the interface with simple call methods such as:

number1 = number1.add(number2)

      


The problems that I had

The problem occurs when trying to implement interface methods for a specific class.
I thought Java realized that the "AnyNumber" type can be any type that implements the interface, so I tried to declare methods like this:

public IntegerNumber add(IntegerNumber other) {
    return new IntegerNumber(this.value + other.value);
}

      

But Eclipse was complaining that I didn't implement the following method:

AnyNumber add(AnyNumber other)

      

Then I could just change the return and argument types to "AnyNumber", but I soon realized that I couldn't access the "value" variable inside "other" because the interface didn't define it.

So, I thought that maybe I needed an abstract class that defined all the required fields and abstract methods for all different types of numbers, but I'm not sure if this is a good approach, since integers are just needed for one field, while while rationality needs two, and a complex need two, but I would call them different. Also, something keeps telling me that the interface makes a lot of sense in this situation.

.. So, though, maybe I needed generics. But I don't fully understand them yet, and all my attempts to use them to solve this problem have failed terribly.

Help

I realized that I couldn't solve this on my own ... I really want to continue working on this project and learn how to deal with situations like this in Java.

Your help is greatly appreciated!

+3


source to share


1 answer


When you implement an interface method, the parameter types must be exactly the same as in the interface. The return type does not have to be the same - it can be subclassed (this is called covariance). But parameter types cannot be subclasses (this is called contravariance, and Java does not allow).

The reason is that nothing prevents different types of objects from AnyNumber

being different.

AnyNumber n1 = new IntegerNumber(123);
AnyNumber n2 = new RealNumber(23.4);

      

Then you could say:

IntegerNumber n3 = n1.add(n2);

      



Or, you can pass n1

and n2

as parameters to some other method that tries to do this. The point here is that since n1

u are n2

declared as AnyNumber

, the compiler cannot tell at this point whether both objects are of the same class.

This means that if you require both objects to be of the same class, you will have to enforce it yourself at runtime.

Your implementation class should look like

public class IntegerNumber implements AnyNumber {

    public IntegerNumber add(AnyNumber other) {  // NOTE: Must be AnyNumber!!
        IntegerNumber otherInteger = (IntegerNumber)other;
            // will throw a ClassCastException at runtime if "other" is, say, RealNumber
        ...
    }
}

      

The rest of the method can use otherInteger

which the compiler thinks it will be IntegerNumber

at this point since you checked. There are other things you can do; you can use instanceof

to inspect and manage the exception you are throwing (or whatever else you are doing), instead of throwing the exception. You can also add mechanisms to handle the addition of different types AnyNumber

if you choose.

+2


source







All Articles