1

I have the following hierarchy of type-ized interfaces and one implementation:

public interface Operation<T> {

    T add(T object1, T object2);
}

public interface OperationNumeric<T extends Number> extends Operation<T> {

        T add(T number1, T number2);
}

And this implementation with an overloaded method add():

public class OperationFloat implements OperationNumeric<Float> {

        public Float add(Float number1, Float number2) {
                return number1+number2;
        }

        public Float add(Float number1, Integer number2) {
                return number1+number2;
        }
    }

Now I have a method that uses OperationNumericInterface:

 public Number calculate(Number operand1, Number operand2, OperationNumeric operand) throws Exception {
      return operand.add(operand1, operand2);
}

Now I'm trying to call calculate() intentionally with two different types Float and Integer like this:

  try {
       calculate(2.2f,2, new OperationFloat());
  } catch (Exception e) {
        System.out.println(e.getMessage());
 }

Once I call it, Java keeps referring to add(Float, Float) method and cannot see the overloaded method add(Float, Integer).

IDEA suggests "Unchecked call to add(T, T) as a member of raw type ..."

How do I modify my interfaces/classes to be able to use overloaded method as I want?

bakoyaro
  • 2,550
  • 3
  • 36
  • 63
Andreas Gelever
  • 1,736
  • 3
  • 19
  • 25
  • This is interesting. I thought Java would see `2` as int and use the `Integer` signature. What happens when you wrap the `2` in `Integer(2)`? – Hubert Grzeskowiak Sep 29 '16 at 13:32
  • 6
    `add(Float, Integer)` is not part of the interface. Why should Java call this method if you operate on an `OperationNumeric` interface? – Turing85 Sep 29 '16 at 13:32
  • Possible duplicate of [Overloaded method selection based on the parameter's real type](http://stackoverflow.com/questions/1572322/overloaded-method-selection-based-on-the-parameters-real-type) – Tom Sep 29 '16 at 13:34
  • That Q&A is somewhat related to this one, as you should know how method calls get "selected". But Turing85 is also right, that the compile won't see the overloaded version, as it isn't part of the interface. – Tom Sep 29 '16 at 13:36

2 Answers2

3

The base flaw in your solution is that both operands within the Operation interface must be of the same type. Thus, method Float add(Float, Integer) is not visible through the interface OperationNumeric<Float> (since Integer and Float have no direct inheritance-relation).

If you want to use different types for both parameter, you would actually need three generic parameters (one for each parameter and one for the return type). This would result in an interface like this:

/**
 * @param <O> first parameter-type of operation
 * @param <P> second parameter-type of operation
 * @param <R> result-type of operation
 */
public interface OperationNumeric<O extends Number
                                  , P extends Number
                                  , R extends Number> {
    R add(O number1, P number2);
}

This interface would then be implemented like this:

OperationNumeric<Float, Float, Float> floatAddition
    = (Float f1, Float f2) -> f1 + f2);

OperationNumeric<Float, Integer, Float> floatIntAddition
    = (Float f, Integer i) -> f + i;

OperationNumeric<Number, Number, Number> numberAddition
    = (Number n1, Number n2) -> n1.doubleValue() + n2.doubleValue();
...

Notice the example for numberAddition. This is most probably the closest to what you really want to achieve. If you take a look at the implementation, you will see that the operation is done on double-values, thus you loose some type-specific properties (e.g. the addition is not done within a circular arithmetic, you will not round to the next-lowest int value when dividing,...). One could possibly take care of this by adding a bunch of instanceof checks like this:

        if ((n1 instanceof Integer) && (n2 instanceof Integer)) {
            return (((Integer) n1) + ((Integer) n2));
        }
Turing85
  • 18,217
  • 7
  • 33
  • 58
0

If you are using a variable object declared by the interface OperationNumeric operand, you only can use the methods declared in such interface. The rest of method of the instantiated object are not visible. Try defining the object properly like:

if (operand instanceof OperationFloat && operand2 instanceof Integer) {
    ((OperationFloat) operand).add(operand1, (Integer)operand2);
}
Paco Abato
  • 3,920
  • 4
  • 31
  • 54