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


88

如果我有两个接口,它们的用途完全不同,但是具有相同的方法签名,那么我如何使一个类同时实现两个接口,而又不被迫编写一个同时为两个接口服务的方法并在该方法中编写一些复杂的逻辑检查调用的对象类型并调用适当代码的实现?

在C#中,这可以通过所谓的显式接口实现来克服。Java中有什么等效方法吗?


37
一个类必须实现具有相同签名的两个方法来执行不同的操作时,那么您的类几乎肯定可以执行太多的操作。
Joachim Sauer 2010年

15
上面的内容可能并不总是适用于IMO。有时,在单个类中,您需要必须与外部合同确认(从而限制签名)的方法,但这些方法具有不同的实现。实际上,这些是设计非平凡类时的常见要求。重载和重载是必要的机制,允许方法执行在签名上可能不会不同或略有不同的不同事情。我在这里所具有的一点限制是它不允许子类化/甚至不允许签名上的丝毫变化。
Bhaskar'4

1
我很想知道这些类和方法是什么。
Uri 2010年

2
我遇到过这样的情况,其中旧有的“ Address”类实现了具有getName()方法的Person和Firm接口,只是从数据模型返回String。一个新的业务要求指定Person.getName()返回一个格式为“姓氏,给定名称”的字符串。经过大量讨论,数据已改为在数据库中重新格式化。
belwood'1

12
仅仅声明班级几乎肯定在做太多事情并不具有建设性。我现在有这种情况,因为我的班级有来自2个不同接口的方法名称冲突,而我的班级并没有做太多事情。目的很相似,但是做的事情略有不同。不要通过指责提问者实施不良的软件设计来试图捍卫显然严重残障的编程语言!
j00hi 2014年

Answers:


75

不,没有办法在Java的一个类中以两种不同的方式实现相同的方法。

这可能导致许多令人困惑的情况,这就是Java禁止使用它的原因。

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

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

您可以做的是在两个类中组成一个类,每个类实现一个不同的接口。这样一类将具有两个接口的行为。

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

9
但是通过这种方式,我无法在需要接口引用(ISomething或ISomething2)的地方传递CompositeClass的实例?我什至不能期望客户端代码能够将实例强制转换为适当的接口,所以我不会因为这种限制而失去一些东西吗?还要注意,以这种方式,当编写实际实现各个接口的类时,我们失去了将代码放入单个类的好处,这有时可能会成为一个严重的障碍。
Bhaskar'4

9
@Bhaskar,您提出正确的观点。我最好的建议是在该类中添加ISomething1 CompositeClass.asInterface1();ISomething2 CompositeClass.asInterface2();方法。然后,您可以从复合类中获得一个或另一个。但是,没有很好的解决方案。
jjnguy 2010年

1
说到这可能导致的混乱情况,您能举个例子吗?我们是否可以不将添加到方法名称中的接口名称视为可以避免冲突/混乱的额外范围解析?
Bhaskar '04

@Bhaskar如果我们的班级坚持单一责任原则,那就更好了。如果存在一个实现两个非常不同的接口的类,我认为应该对设计进行重新设计以拆分这些类,以承担单个职责。
Anirudhan J

1
允许public long getCountAsLong() implements interface2.getCount {...}[如果接口要求一个long类但该类的用户期望的情况下int]或private void AddStub(T newObj) implements coolectionInterface.Add[假定collectionInterface有一个canAdd()方法,并且对于该类的所有实例都返回false]之类的东西,真是令人困惑?
2013年

13

用Java没有解决这个问题的真正方法。您可以使用内部类作为解决方法:

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

尽管它不允许从AlfaBetato进行强制Beta转换,但通常来说,低调是邪恶的,并且如果可以预期Alfa实例也经常具有Beta某些方面,并且出于某种原因(通常是优化是唯一有效的原因),您希望能够将其转换为Beta,你可以做一个子接口AlfaBeta asBeta()它。


你是说匿名阶级而不是内部阶级吗?
Zaid Masud 2012年

2
@ZaidMasud我的意思是内部类,因为它们可以访问封闭对象的私有状态。这些内部类当然也可以是匿名的。
gustafc 2012年

11

如果遇到此问题,则最有可能是因为您在应使用委托的地方使用了继承。如果您需要为同一基础数据模型提供两个不同的(尽管相似)接口,则应使用一个视图使用其他接口廉价地提供对数据的访问。

为了给出后一种情况的具体示例,假设您想同时实现CollectionMyCollection(不继承Collection并且具有不兼容的接口)。您可以提供Collection getCollectionView()MyCollection getMyCollectionView()函数,CollectionMyCollection使用相同的基础数据提供和的轻量级实现。

对于前一种情况...假设您确实想要一个整数数组和一个字符串数组。而不是同时从List<Integer>和继承List<String>,您应该具有type的一个成员和type的List<Integer>另一个成员List<String>,并引用这些成员,而不是尝试从这两个成员继承。即使您只需要一个整数列表,在这种情况下也最好使用组合/委托而不是继承。


我不这么认为。您会忘记需要实现不同接口以与它们兼容的库。与使用自己的代码运行时相比,您可以更频繁地使用多个冲突的库来运行此程序。
nightpool '16

1
@nightpool如果使用多个库,每个库都需要不同的接口,则单个对象仍不需要实现两个接口;您可以使对象具有用于返回两个不同接口中的每个接口的访问器(并在将对象传递到基础库之一时调用适当的访问器)。
Michael Aaron Safyan '16

1

“经典的” Java问题也影响了我的Android开发...
原因似乎很简单:
您必须使用更多的框架/库,更容易使事情失去控制...

就我而言,我有一个BootStrapperApp类继承自android.app.Application
而同一类也应实现MVVM框架的Platform接口,以便进行集成。
方法冲突发生在getString()方法上,该方法由两个接口声明,并且在不同的上下文中应具有不同的实现。
解决方法(ugly..IMO)使用内部类来实现所有平台方法,只是因为一个较小的方法签名冲突...在某些情况下,甚至根本不使用这种借用的方法(但会影响主要的设计语义)。
我倾向于同意C#风格的显式上下文/命名空间指示很有帮助。


1
直到我开始将Java用于Android开发之前,我才意识到C#是如何考虑周到且功能丰富的。我认为那些C#功能是理所当然的。Java缺少太多功能。
该死的蔬菜

1

我想到的唯一解决方案是将引用对象用于要使多个接口交互的对象。

例如:假设您有2个要实现的接口

public interface Framework1Interface {

    void method(Object o);
}

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

您可以将它们封装到两个Facador对象中:

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

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

最后,您想要的课程应该是

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

您现在可以使用getAs *方法“公开”您的类


0

您可以使用适配器模式来使它们起作用。为每个接口创建两个适配器并使用它。它应该解决问题。


-1

当您完全控制所有相关代码并可以预先实现时,一切都会很好。现在想象一下,您已经有一种方法在许多地方使用了现有的公共类

public class MyClass{

    private String name;

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

    public String getName(){
        return name;
    }
}

现在,您需要将其传递给现成的WizzBangProcessor,它需要类来实现WBPInterface ...,它也具有getName()方法,但是该接口希望该方法返回类型的名称,而不是您的具体实现。 Wizz Bang Processing公司。

在C#中这将是小菜一碟

public class MyClass : WBPInterface{

    private String name;

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

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

    public String getName(){
        return name;
    }
}

在Java Tough中,您将必须确定现有已部署代码库中需要从一个接口转换为另一个接口的每个点。确保WizzBangProcessor公司应该使用getWizzBangProcessName(),但它们也是开发人员。在他们的上下文中,getName很好。实际上,在Java之外,大多数其他基于OO的语言都支持此功能。Java很少强制使用相同的方法NAME来实现所有接口。

大多数其他语言的编译器很乐意接受一条指令说“该类中的此方法与该方法的签名在此已实现接口中的实现相匹配”。毕竟,定义接口的全部要点是允许将定义从实现中抽象出来。(甚至没有让我开始在Java接口中使用默认方法,更不用说默认覆盖了……。因为可以肯定的是,为公路车设计的每个组件都应该能够撞上飞行车并且可以正常工作-嘿它们都是汽车...我敢肯定,默认的俯仰和侧倾输入不会影响您的卫星导航的默认功能,因为汽车只会偏航!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.