6

I have some class public class myClass implements A, B where A and B both contain a method public int doSomething();, but A.doSomething is specified by the interface to do something different than B.doSomething.

I have read Two interfaces with same method signature implemented in Java class, but that doesn't really address my problem, because the methods are overriden to do the same thing, but as I said above, my question is about when they are specified in the interfaces to do different things.

For example, suppose A.doSomething() is supposed to return 0, while B.doSomething() is supposed to throw an exception, and violating either one wold cause problems for methods that are supposed to take them as parameters.

Is there any way to do this in java? If so, how would one actually do it?

Community
  • 1
  • 1
AJMansfield
  • 4,039
  • 3
  • 29
  • 50
  • You have no other way than to change one of the signature. You can't have two methods with same signature in the same class. – Rohit Jain Dec 05 '12 at 18:58
  • In .NET you can implement interface members explicitly, so that they appear private unless the instance is cast to the particular interface type. So you could implement both implementations explicitly. Does Java have a similar construct? – jrummell Dec 05 '12 at 19:38
  • @jrummell No, methods from interfaces *must* be declared public. Also, having two methods with the exact same signature (return type, name, and parameters/parameter order are all the same), even if the accessors are different, *and even if one is declared statically and the other non-statically*, will give you compiler errors – Brian Dec 05 '12 at 19:40
  • @Brian it's good to know these kind of differences when switching between various platforms. Thanks for the information. – jrummell Dec 05 '12 at 19:44

3 Answers3

5

As per JLS (This is similar case like what you want, may not exact)

interface Fish { int getNumberOfScales(); }
interface StringBass { double getNumberOfScales(); }
class Bass implements Fish, StringBass {
    // This declaration cannot be correct, no matter what type is used.
    public ??? getNumberOfScales() { return 91; }
}

It is impossible to declare a method named getNumberOfScales whose signature and return type are compatible with those of both the methods declared in interface Fish and in interface StringBass, because a class cannot have multiple methods with the same signature and different primitive return types (§8.4).

Unless you change your design by adding proxy (or) method signature it is not possible to do what you are expecting.

kosa
  • 65,990
  • 13
  • 130
  • 167
2

You might be able to use a Proxy instance to do this. See this question for info on Proxy (particularly, the second part of the answer.)

The InvocationHandler that you write would check to see which interface is being used to call the method and delegate to the appropriate method inside your object. Here's what your implementation looks like:

public class MyClass {
    // Note that we aren't implementing the interfaces anymore

    public int doSomethingForA() {
        return 0;
    }

    public int doSomethingForB() {
        throw new IllegalArgumentException();
    }
}

Then your InvocationHandler:

public class MyClassInvocationHandler implements InvocationHandler {

    private MyClass target;

    public MyClassInvocationHandler(MyClass target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (method.getDeclaringClass().equals(InterfaceA.class))
                return MyClass.getMethod("doSomethingForA").invoke(target, args);
            else if (method.getDeclaringClass().equals(InterfaceB.class))
                return MyClass.getMethod("doSomethingForB").invoke(target, args);
            else
                throw new UnsupportedOperationException("Unsupported interface: " + method.getDeclaringClass());
        } catch (NoSuchMethodException ex) {
            throw new UnsupportedOperationException("Method not found", ex);
        } catch (IllegalAccessException ex) {
            throw new UnsupportedOperationException("Method was not public", ex);
        } catch (InvocationTargetException ex) {
            // May throw a NullPointerException if there is no target exception
            throw ex.getTargetException();
        }
    }
}

Then to create the proxy, you'll pass in both interfaces:

Proxy.newProxyInstance(null, new Class<?>[] { InterfaceA.class, InterfaceB.class }, new MyClassInvocationHandler(mc));

I think this will work. When you call it using one interface or the other:

MyClass mc = new MyClass();
Object proxy = Proxy.newProxyInstance(null, new Class<?>[] { InterfaceA.class, InterfaceB.class }, new MyClassInvocationHandler(mc));
InterfaceA a = (InterfaceA) proxy;
a.doSomething();
InterfaceB b = (InterfaceB) proxy;
b.doSomething();

Then it should pass in Method objects with different declaring classes. I'm not sure if this is how it works though, so this will need to be tested.

Community
  • 1
  • 1
Brian
  • 17,079
  • 6
  • 43
  • 66
  • **Disclaimer:** this is *really* hacky and you should only do this if it's absolutely necessary, i.e. you can't change either of the interfaces or create two different implementations due to some wild data constraints. – Brian Dec 05 '12 at 19:42
0

It's only possible (different return types) if you overload methods

public interface IA {
int doSomething();
}

public interface IB {
void doSomething(String value) throws Exception;
}

public class B  implements IB, IA{
    @Override
    public void doSomething(String value) throws Exception {
        throw new Exception(value);
    }

    @Override
    public int doSomething() {
        return 0;
    }
}
Matusz
  • 44
  • 4