【问题标题】:Can I force abstract methods to be protected when someone overrides them?当有人重写抽象方法时,我可以强制保护它们吗?
【发布时间】:2015-04-24 15:27:11
【问题描述】:

在我的抽象类中,我有这样的东西:

public Object methodIWantToExpose(){
  // ... 
  methodIDontWantExposed()
  // ...
}

protected abstract void methodIDontWantExposed();

问题是,我想强制扩展 methodIDontWantExposed() 的人使其受到保护,因为我不希望扩展类同时暴露 methodIDontWantExposed 和 methodIWantToExpose。

有没有办法做到这一点(或可能避免我的问题的不同方法)?

【问题讨论】:

  • 您想确保任何具体的子类都不会公开 methodIDontWantExposed?
  • 子类可以导出任何可以从超类访问的行为。持有该行为的方法的访问级别无关紧要。
  • 就像在现实世界中一样,父母只能做很多事情来阻止他们的孩子做一些愚蠢的事情。
  • 恐怕你正在尝试的是你最好避免的事情的症状。因此,也许有更好的设计解决方案可以解决您的问题。如果您愿意分享您真正想要实现的目标,您可能会得到比简单的“否”更好的答案。
  • 想象一下,如果这是可能的。那么是什么阻止子类添加public void callMethodIDontWantExposed() {methodIDontWantExposed();} 呢?

标签: java access-modifiers


【解决方案1】:

在某些情况下,您可以完全避免使用抽象方法(甚至是子类化),而是可以使用在枚举中定义的 strategy

当然,使用的枚举实例仍然可以公开,但至少可以保证唯一允许的行为是您的枚举中定义的行为。

所以你的解决方案是这样的:

enum Behaviour {
  ONE() {
     public Object doSomething() { return "ONE"; }
  };

   public abstract Object doSomething();
}

// and your class

public abstract class MyClass {
   private final Behaviour behaviour;

   protected MyClass( Behaviour behaviour ) {
     this.behaviour = behaviour;
   }

   public final void methodIWantToExpose() {
      // ...
      behaviour.doSomething();
      // ...
   } 
}

【讨论】:

    【解决方案2】:

    不,子类在覆盖方法时总是可以扩大访问范围。没有办法阻止这种情况。然而,通常当我重写一个方法时,我很少将可见性从受保护更改为公开。仔细记录该方法的目的可能足以让实施者相信在这种情况下这将是一个坏主意。

    如果您真的想以私有方式封装行为,您可以按照以下方式进行操作:

    abstract class YourClass {
        private HandlerInterface unexposedHandler;
    
        YourClass(HandlerInterface handler) {
            unexposedHandler = handler;
        }
    
        public Object methodIWantToExpose(){
            // ... 
            handler.methodIDontWantExposed();
            // ...
        }
    }
    

    使用 Java 8,您甚至可以将 HandlerInterface 设为函数式接口并方便地使用 lambda,如下所示:

    class YourSubClass extends YourClass {
        YourSubClass() {
            super(() -> {
                System.out.println("This is the unexposed code");
            });
        }
    
        ...
    }
    

    【讨论】:

    • 在我的情况下这不值得(我只会在文档中添加警告),但我会接受这个答案,因为它提供了一个实际的解决方法。
    • 对。我同意在这种情况下,正确的文档可能(不幸的是)是最好的方法。
    • 但是子类仍然可以通过覆盖该构造函数来存储引用,然后在需要时提供访问权限吗?
    • 当然可以。事实上,任何子类必须“覆盖”该构造函数并显式调用它。
    【解决方案3】:

    你不能,因为增加方法的可见性不会破坏基类契约。

    看看Why Java allows increasing the visibility of protected methods in child class?

    【讨论】:

    • 它可能不会破坏一般的“基类”契约,但一个基类可以完全合法地定义一个禁止此类暴露的契约(在这种情况下,这种可见性将违反该契约)。这种事情在语言中是允许的,因为它的设计目的不是代表基类执行多种合同。
    【解决方案4】:

    这取决于您是否希望您的类的用户能够覆盖该方法。如果你让他们重写它,他们总是可以用公共方法重写它。

    一些方法可以避免让他们覆盖该方法:

    1. 已在抽象类中实现并使其成为最终的,因此不能被覆盖。

    2. 将其设为私有包,并在属于同一包的多个子类之一中实现它。由于您将是实现它的人,因此您可以将其保持为包私有,或将其更改为最终受保护的,这样您的包外的子类将无法使用公共方法覆盖它。

    【讨论】:

      【解决方案5】:

      不,你不能。如果它是一个具体的方法,你可以将它设为私有。在您的情况下,没有聪明的方法可以阻止扩展类将其公开(您当然可以评论该方法,说它不应该公开)。

      【讨论】:

        【解决方案6】:

        没有。子类总是可以使方法更加公开。

        即使他们不能用你在课堂上的方法做到这一点,他们总是可以这样写:

        public void callsMethodIDontWantExposed() {
            methodIDontWantExposed();
        }
        

        ...所以你会遇到同样的情况。

        【讨论】:

        • 如果我的抽象类的用户(出于某种奇怪的原因)真的想要这样做,我并不反对公开该方法。我只需要一种方法让他更容易保护它而不是公开它。换句话说,我希望人们对其进行保护,除非他们真的知道自己在做什么,因为将其公开可能不是他们需要的。
        • 我建议您那时可能只需要相关文档。
        • @justsomeusername 在我看来,这听起来好像你在试图防止别人犯下粗心或无知的错误。根据我的经验,重写方法是为了引入多态变化,我什至很少考虑增加可见性。在这种情况下,我看不到文档是有益的。你真的想浪费精力来记录你一半的受保护方法:“如果你覆盖它,除非你需要它,否则不要公开它。”?您付出的任何努力都无法保证人们在使用/继承您的课程时不会犯错误。
        • 这里值得注意的是 Java 和 C# 之间的区别:在 C# 中,采用不公开类型的参数或返回此类类型的参数的方法不能公开; Java没有这样的限制,甚至允许从外部代码调用这样的方法。
        • @supercat:确实。它也存在于类型层次结构中——在 C# 中,你不能从内部类型派生公共类型,而 Java 让我们做同样的事情,这样你不一定能“看到”所有继承的成员,即使它们是公共的.就我个人而言,我发现 C# 方法更加一致。
        猜你喜欢
        • 1970-01-01
        • 2010-09-19
        • 2018-05-08
        • 1970-01-01
        • 1970-01-01
        • 2018-07-12
        • 2013-06-28
        • 2011-11-16
        • 1970-01-01
        相关资源
        最近更新 更多