接口实现中的 Java-Method 名称冲突

如果我有两个接口,两个接口的用途完全不同,但使用相同的方法签名,我如何使一个类实现既不被迫编写一个单独的方法来为两个接口服务,又不被迫在方法实现中编写一些复杂的逻辑来检查调用的对象类型并调用正确的代码?

在 C # 中,这被称为显式接口实现所克服。在 Java 中有没有相同的方法?

41829 次浏览

No, there is no way to implement the same method in two different ways in one class in Java.

That can lead to many confusing situations, which is why Java has disallowed it.

interface ISomething {
void doSomething();
}


interface ISomething2 {
void doSomething();
}


class Impl implements ISomething, ISomething2 {
void doSomething() {} // There can only be one implementation of this method.
}

What you can do is compose a class out of two classes that each implement a different interface. Then that one class will have the behavior of both interfaces.

class CompositeClass {
ISomething class1;
ISomething2 class2;
void doSomething1(){class1.doSomething();}
void doSomething2(){class2.doSomething();}
}

If you are encountering this problem, it is most likely because you are using inheritance where you should be using delegation. If you need to provide two different, albeit similar, interfaces for the same underlying model of data, then you should use a view to cheaply provide access to the data using some other interface.

To give a concrete example for the latter case, suppose you want to implement both Collection and MyCollection (which does not inherit from Collection and has an incompatible interface). You could provide a Collection getCollectionView() and MyCollection getMyCollectionView() functions which provide a light-weight implementation of Collection and MyCollection, using the same underlying data.

For the former case... suppose you really want an array of integers and an array of strings. Instead of inheriting from both List<Integer> and List<String>, you should have one member of type List<Integer> and another member of type List<String>, and refer to those members, rather than try to inherit from both. Even if you only needed a list of integers, it is better to use composition/delegation over inheritance in this case.

There's no real way to solve this in Java. You could use inner classes as a workaround:

interface Alfa { void m(); }
interface Beta { void m(); }
class AlfaBeta implements Alfa {
private int value;
public void m() { ++value; } // Alfa.m()
public Beta asBeta() {
return new Beta(){
public void m() { --value; } // Beta.m()
};
}
}

Although it doesn't allow for casts from AlfaBeta to Beta, downcasts are generally evil, and if it can be expected that an Alfa instance often has a Beta aspect, too, and for some reason (usually optimization is the only valid reason) you want to be able to convert it to Beta, you could make a sub-interface of Alfa with Beta asBeta() in it.

The "classical" Java problem also affects my Android development...
The reason seems to be simple:
More frameworks/libraries you have to use, more easily things can be out of control...

In my case, I have a BootStrapperApp class inherited from android.app.Application,
whereas the same class should also implement a Platform interface of a MVVM framework in order to get integrated.
Method collision occurred on a getString() method, which is announced by both interfaces and should have differenet implementation in different contexts.
The workaround (ugly..IMO) is using an inner class to implement all Platform methods, just because of one minor method signature conflict...in some case, such borrowed method is even not used at all (but affected major design semantics).
I tend to agree C#-style explicit context/namespace indication is helpful.

The only solution that came in my mind is using referece objects to the one you want to implent muliple interfaceces.

eg: supposing you have 2 interfaces to implement

public interface Framework1Interface {


void method(Object o);
}

and

public interface Framework2Interface {
void method(Object o);
}

you can enclose them in to two Facador objects:

public class Facador1 implements Framework1Interface {


private final ObjectToUse reference;


public static Framework1Interface Create(ObjectToUse ref) {
return new Facador1(ref);
}


private Facador1(ObjectToUse refObject) {
this.reference = refObject;
}


@Override
public boolean equals(Object obj) {
if (obj instanceof Framework1Interface) {
return this == obj;
} else if (obj instanceof ObjectToUse) {
return reference == obj;
}
return super.equals(obj);
}


@Override
public void method(Object o) {
reference.methodForFrameWork1(o);
}
}

and

public class Facador2 implements Framework2Interface {


private final ObjectToUse reference;


public static Framework2Interface Create(ObjectToUse ref) {
return new Facador2(ref);
}


private Facador2(ObjectToUse refObject) {
this.reference = refObject;
}


@Override
public boolean equals(Object obj) {
if (obj instanceof Framework2Interface) {
return this == obj;
} else if (obj instanceof ObjectToUse) {
return reference == obj;
}
return super.equals(obj);
}


@Override
public void method(Object o) {
reference.methodForFrameWork2(o);
}
}

In the end the class you wanted should something like

public class ObjectToUse {


private Framework1Interface facFramework1Interface;
private Framework2Interface facFramework2Interface;


public ObjectToUse() {
}


public Framework1Interface getAsFramework1Interface() {
if (facFramework1Interface == null) {
facFramework1Interface = Facador1.Create(this);
}
return facFramework1Interface;
}


public Framework2Interface getAsFramework2Interface() {
if (facFramework2Interface == null) {
facFramework2Interface = Facador2.Create(this);
}
return facFramework2Interface;
}


public void methodForFrameWork1(Object o) {
}


public void methodForFrameWork2(Object o) {
}
}

you can now use the getAs* methods to "expose" your class

You can use an Adapter pattern in order to make these work. Create two adapter for each interface and use that. It should solve the problem.

All well and good when you have total control over all of the code in question and can implement this upfront. Now imagine you have an existing public class used in many places with a method

public class MyClass{


private String name;


MyClass(String name){
this.name = name;
}


public String getName(){
return name;
}
}

Now you need to pass it into the off the shelf WizzBangProcessor which requires classes to implement the WBPInterface... which also has a getName() method, but instead of your concrete implementation, this interface expects the method to return the name of a type of Wizz Bang Processing.

In C# it would be a trvial

public class MyClass : WBPInterface{


private String name;


String WBPInterface.getName(){
return "MyWizzBangProcessor";
}


MyClass(String name){
this.name = name;
}


public String getName(){
return name;
}
}

In Java Tough you are going to have to identify every point in the existing deployed code base where you need to convert from one interface to the other. Sure the WizzBangProcessor company should have used getWizzBangProcessName(), but they are developers too. In their context getName was fine. Actually, outside of Java, most other OO based languages support this. Java is rare in forcing all interfaces to be implemented with the same method NAME.

Most other languages have a compiler that is more than happy to take an instruction to say "this method in this class which matches the signature of this method in this implemented interface is it's implementation". After all the whole point of defining interfaces is to allow the definition to be abstracted from the implementation. (Don't even get me started on having default methods in Interfaces in Java, let alone default overriding.... because sure, every component designed for a road car should be able to get slammed into a flying car and just work - hey they are both cars... I'm sure the the default functionality of say your sat nav will not be affected with default pitch and roll inputs, because cars only yaw!