扩展三个在Java中实现接口的类

Posted

技术标签:

【中文标题】扩展三个在Java中实现接口的类【英文标题】:Extend three classes that implements an interface in Java 【发布时间】:2021-03-17 16:34:50 【问题描述】:

我使用一个带有接口的库,IFC,它由三个类 A、B 和 C 实现。

我想自己实现方法 M,它在 IFC 中定义,并在 A、B 和 C 中实现,它应该覆盖 A、B 和 C 中的相应方法。

这可能吗?

在Java 中,你实现,而不是扩展,一个接口,你只能扩展一个类。这使我得出结论,我想做的事情是不可能的。

相反,我需要创建:

class MyA extends A 

    myM () 
        // Do my stuff
        super.M();
    

然后是 B 和 C 的两个相同的类,每个都有相同的 myM 方法,但这感觉真的很笨拙和丑陋。还有其他方法吗?

【问题讨论】:

你为什么打电话给super.M();?有必要吗? 我没有看到扩展 A、B 和 C 的替代方法。无论如何,如果 //do my stuff 对所有三个类都相同,那么您可以编写一个实现 //do my stuff 的类 D 和在MyA 中引用它等等。这至少会集中//do my stuff的实现并减少冗余。 也许您想使用代理/适配器来覆盖一种方法并将所有其他方法委托给底层实例。然后,您可以使用它来包装 ABC 的实例。 这个问题是discussed on Meta。 看起来像一个 xy 问题:由于某种原因,您没有详细说明,您想更改现有接口/类(即 IFC、A、B、C)的规范 - 这可以'没有完成,如你所知 ;) 可能有一些选项(@Scratte 建议的 fi 仪器或包装器),这取决于你 真正 想要实现的目标......现在我们又回到了失踪细节 ;)。基本上,您必须重新考虑和重新评估您的要求,然后返回缺少的细节。 【参考方案1】:

使用这个简化的库实现,使用method()而不是M()

interface IFC 
  void method();


class A implements IFC 
  public void method() 
    System.out.println("method in A");
  ;

正如akuzminykh 在他们的comment 中提到的那样

您将编写一个具有 M 和 IFC 字段的类。在 M 中,你会做你的事情,然后调用 IFC 的 M,其中 IFC 可以是 A、B 或 C。所以你会将 A、B 和 C 的所有实例包装在这样一个类中,这将避免扩展 A、B和 C.

这样做的好处是你只需要在初始化你的类时包装它们:IFC wrapIFC = new IFCWrapper(new A());。完成后,您无需对变量 wrapIFC 进行任何不同于使用 new A() 初始化的任何变量的处理:

class IFCWrapper implements IFC 
  IFC ifc;

  IFCWrapper(IFC ifc) 
    this.ifc = ifc;
  

  public void method() 
    always();
    ifc.method();
  

  void always() 
    System.out.println("wrapper method");
  


您还可以创建一个单独的独立(抽象?)类或接口,您可以调用它而不是调用method() 传递您的实例。不利的一面是,每次要在实例上调用 method() 时,您都必须记住调用它。我选择了使用Runnable 传递此示例所需的任何额外内容的选项:

class IFCExt 
  static Runnable defaultRun = () -> System.out.println("default extra");
  static void method(Runnable toRun, IFC ifc) 
    toRun.run();
    ifc.method(); 
  


两种变体的运行示例:

public class Test 
  public static void main(String[] args)  
    IFC wrapIFC = new IFCWrapper(new A());
    wrapIFC.method();

    System.out.println();

    IFC myIFC = new A();
    IFCExt.method(IFCExt.defaultRun, myIFC);
    IFCExt.method(() -> System.out.println("anything extra"), myIFC); // anything you'd want
  

打印:

wrapper method
method in A

default extra
method in A
anything extra
method in A

【讨论】:

很好的例子,然而,这个解决方案根据用例有问题:如果除了调用 M 之外还应该使用 A、B 和 C 怎么办?如果 A、B 和 C 不仅有 M,而且还有 M1、M2 和 M3,会怎样?如果 A 有 M4 而 B 没有怎么办?如果将 A、B 和 C 的实例传递给库的其他方法怎么办?除了使用单个方法 M 的简单用法之外,您还需要编写很多代码。它会工作吗?是的。会好看吗?根据用例的不同,这会很糟糕,而您只需扩展 A、B 和 C 就会更快乐。 @akuzminykh 我同意采用的解决方案取决于用例。有趣的情况是 A 有 M4,但 B 没有。在这种情况下,如果使用变量类型作为接口进行初始化,则无法调用 M4。在将它们传递给库的其他方法的问题上,code11 的 other Answer 成为一个非常有吸引力的解决方案,特别是如果人们希望它在库内部工作。如果不是,我同意您仅按照问题本身的建议扩展课程:) 很高兴您也看到了这些问题......这就是“需要细节或清晰性”出现的地方。 ;) 但正如我在comment on meta 中所说的那样:不足以关闭这个问题 IMO,但这样做绝对可以理解。 @akuzminykh 不足以结束这个问题 IMO .. 绝对值得商榷 - 但答案中的单纯体操是一个很好的指标,表明细节太少而无法提供好的答案,IMO :) 必须有一个非常令人信服的理由(除了问题中感觉笨拙和丑陋,这也是有争议的:) 投资于复杂性。 这是如何使用代理模式的一个很好的例子。乍一看,这种大规模的后果可能并不明显。本质上,您必须在库提供的任何 API 之上创建一个抽象层。返回或传递的 A、B、C 的所有实例都必须被包装或解包。在大型库中,这可能意味着包装数百个函数。更糟糕的是,必须维护这个抽象层。如果库开发人员添加了一个返回 A、B、C 的新函数,那么它也必须被包装。【参考方案2】:

我以前也遇到过这样的情况。如果您只是要在库外部使用 A、B 和 C,代理/适配器一切都很好,您可能希望在内部使用时替换从 A、B、C 调用的 M() /em> 也是。

我遇到的示例是一个使用非线程安全集合的遗留库,您只想切换它。我会假设情况和我的一样,你只能访问一个 jar,但没有源,并且你也想更改 M() 的内部使用。在这种情况下,正如许多人指出的那样,通过常规 J​​ava 代码实现这一点是不可能的。

但是,如果代码在您的计算机上,那么您可以使用正常 Java 开发范围之外的两种技术。它们的使用可能会对您正在编写的代码的构建复杂性和维护产生巨大影响。

如果您真的想替换 M() 的每个实例而不创建自己的类,一个可能的解决方案是在运行时交换方法。

有些图书馆声称能够做到这一点。 This question 详细介绍了这项技术,并强调了一个库,HotSwapAgent 可以做到这一点。

另一种解决方案是反编译库的 jar,更改有问题的方法并重新编译。根据库对其他依赖项的使用和构建复杂性,这可能并非易事。然后,您还处于必须维护新更改的 jar 的情况。如果库更新,您必须重复该过程。

【讨论】:

这个答案的 internally 部分似乎是一个很好的卖点。而不是解决这个问题,只需“破解”它:)

以上是关于扩展三个在Java中实现接口的类的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中实现和使用通用的抽象接口

在 Java 中实现动态插件

通用扩展类 AND 在 Kotlin 中实现接口

Java的十三个设计模式

PowerDesigner 不同包中实现接口

Java中实现多线程的两种方式之间的区别