简体   繁体   中英

Java generics issue with overloaded methods

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?

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 (eg 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));
        }

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM