【问题标题】:How to extend a final class in Java如何在 Java 中扩展最终类
【发布时间】:2012-11-08 17:40:05
【问题描述】:

这是我现在真正面临的问题。 我有一个类,比如说Foo,这个类定义了一个名为getBar 的方法,它返回一个Bar 实例。 Bar 类在 Foo 内部定义并声明为 public static final。我想做的是定义一个扩展Foo 的类MyFoo,但我还想通过添加我自己的功能(方法、属性等)来扩展BarMyBar。 我也想让getBar返回MyBar

问题是Bar 是最终的。这是我想要做的说明:

public class Foo {

  Bar bar = new Bar();

  public Bar getBar(){
      return bar;
  } 

   ....

   public static final class Bar {

    } 
}

我想做的是:

public class MyFoo extends Foo {


   public MyBar getBar(){ // here I want to return an instance of MyBar

   }

  public static final class MyBar extends Bar { // That's impossible since Bar is final

  } 
}

我怎样才能做到这一点?

[编辑] 我正在为我的问题添加更多细节。 我实际上正在为 Jenkins 开发一个插件,在搜索之后,我意识到有一个插件提供了我想做的基本功能,FooBar。我想自定义FooBar,所以它可以满足我的需要。 Foo 是插件的主类,并扩展了一个名为 Builder 的类。 Foo 定义了一个名为perform 的方法,其中包含执行构建的所有操作。 Bar 包含用于配置目的的操作,Foo 需要 Bar 来获取这些信息。

所以我想做的是用MyFoo 扩展Foo 并用MyBar 添加一些配置。如您所见,MyFoo 需要通过调用 getBar(); 来获取这些配置

第二件事是我无法控制MyFooMyBar的实例化

阻碍我的是,我无法扩展 Bar。我怎样才能做到这一点?

【问题讨论】:

  • 由于final 修饰符阻止您继承Bar,我建议“包装”它。
  • 如果Barfinal class,通常是有充分理由的,例如也许是因为Foo 的一些不变量需要这样做。所以我不会那样做,或者至少你应该准确地理解所有相关的源代码,并可能重写它。
  • Bar 的开发人员将其设为非最终版本或按照mre 的建议使用委托。
  • 理想情况下,Bar 将实现一个接口IBar,应用程序的其余部分针对该接口进行编码。在一个不完全属于设计内部的简单类上使用final 可能是一种设计气味。 (就像返回给 API 客户端的类一样。)
  • 谢谢你的回答,伙计们。我怎样才能“包裹”酒吧?

标签: java static final


【解决方案1】:

你不能,基本上。看起来设计Bar 的开发人员打算不对其进行扩展——因此您无法对其进行扩展。您将需要寻找一种不同的设计——可能是一种不太使用继承的设计……当然,假设您无法更改现有代码。

(如果不了解这里的真正目标——整个系统的设计,很难给出更具体的建议。)

【讨论】:

  • 好吧,我实际上正在为 Jenkins 开发一个插件,而 Foo 和 Bar 有很多我想扩展的功能。那会阻止我重写 Foo 和 Bar 的相同代码
  • @Dimitri:例如,这并没有给我们足够的信息来了解使用组合而不是继承是否合理。
  • FooBar 是故意设计的,因此您无法扩展它们。你真的别无选择。
【解决方案2】:

您不能扩展声明为final 的类。但是,您可以创建一个名为Wrapper 的中间类。这个 Wrapper 基本上将包含原始类的一个实例,并提供替代方法来根据您想要做的修改对象的状态。

当然,这不会提供对 final 类的私有和受保护字段的访问权限,但是您可以使用它的 getter 和 setter 方法,结合您在 Wrapper 类中声明的新方法和字段来获取你想要的结果,或者比较接近的结果。

如果没有正当理由,另一种解决方案是不声明 final 类。

【讨论】:

    【解决方案3】:

    您不能扩展 final 类 - 这就是 final 关键字的目的。

    但是,有(至少)两种解决方法:

    1. 该类在 Jenkins 项目中,该项目是开源的,因此您只需下载该项目,对类进行所需的更改并重新构建 jar 即可
    2. 有点 hack,但您可以使用像 Javassist 这样的库来替换内存中类的实现

    【讨论】:

    • 为什么有人要让班级决赛?这听起来像是一个可怕的问题,但我是编程新手。我试图扩展 Scanner 类(只是想看看我是否可以),但我发现它不起作用。我的朋友告诉我这是因为 Scanner 课程是期末考试。假设理论上它不是最终的,并且有人确实扩展了它。由于在子类中更改的任何变量都不必更改父类,因此会出现什么问题?等等。idk
    • 我知道它与被覆盖的 Scanner 类有关,但如果它不是最终的,我什至不知道如何覆盖它,我试过了。
    【解决方案4】:

    你不能扩展 final 类,这就是声明类 final 的意义所在。您可以定义最终类继承自的接口和将调用委托给包装的最终类的包装类。那么问题来了,为什么要把这门课放在首位。

    【讨论】:

      【解决方案5】:

      您可能想要的是代理。这很容易实现。这是一篇很棒的文章:http://blog.frankel.ch/the-power-of-proxies-in-java

      【讨论】:

      • 现在无法访问该网站?
      【解决方案6】:

      例如,我们不能在 TreeSet 集合中添加 StringBuffer,因为 TreeSet 不是接口 Comparable 的子对象:

      public class Main(){
      public static void main(String[] args){
         TreeSet treeSetSB = new TreeSet();
         treeSetSB.add(new StringBuffer("A"));  //error, Comparable is not implemented
      }
      

      但我们可以使用包装器来实现 Comparable:

      class myWrap implements Comparable{
          TreeSet treeset;
          public myWrap() {
              this.treeset=new TreeSet<>();
      
          }
          public TreeSet getTreeset() {
              return treeset;
          }
          public void setTreeset(TreeSet treeset) {
              this.treeset = treeset;
          }
      }
      
      
      public class Main(){
      public static void main(String[] args){
      
         myWrap myWrap =new myWrap();
         TreeSet myTrs  =myWrap.getTreeset();
         myTrs.add("b");   // no error anymore
         myTrs.add("a");
         myTrs.add("A");
         System.out.println(myTrs);
      }
      

      【讨论】:

        【解决方案7】:

        如果它实现了一些接口,Decorator Design pattern 可以解决问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-10
          • 1970-01-01
          相关资源
          最近更新 更多