20
abstract class Base{
      protected abstract void a();
}

class Child extends Base{
      @Override
      public void a(){
          //why is this valid
      }
}

为什么我们不能降低能见度,但可以增加它?

我还需要实现模板模式,其中可见的公共方法只能是基类。

例子:

abstract class Base{
      public void callA(){
      //do some important stuff
      a();
      }

      protected abstract void a();
}

class Child extends Base{
      @Override
      public void a(){
          //why is this valid
      }
}

现在如果 java 允许增加可见性,那么有两种公开可见的方法?

我知道界面是一种解决方案,但还有其他出路吗???

4

4 回答 4

28

为什么不允许降低可见性已经在其他回复中解释过(它会破坏父类的合同)。

但是为什么允许增加方法的可见性呢?首先,它不会违反任何合同,因此没有理由不允许它。有时它会很方便,因为在子类中不保护方法是有意义的。

其次,不允许它可能会产生有时无法同时扩展类和实现接口的副作用:

interface Interface1 {
   public void method();
}

public class Parent {
   protected abstract void method();
}

public class Child extends Parent implements Interface1 {
   @Override
   public void method() {
   }
   //This would be impossible if the visibility of method() in class Parent could not be increased.
}

关于你的第二个问题,你无能为力。您必须相信实现子类的人不会做任何破坏您的实现的事情。即使java不允许增加可见性,那仍然不能解决你的问题,因为可以创建一个具有不同名称的公共方法来调用抽象方法:

class Child extends Base{
      @Override
      protected void a(){

      }

      public void a2() {
           a(); //This would have the same problems that allowing to increase the visibility.
      }
}
于 2012-10-08T11:54:13.593 回答
13

如果基类做出关于可见性的承诺,那么子类就不能违反该承诺并仍然满足 Liskov 替换原则。如果承诺被破坏,则在承诺方法暴露的任何情况下,您都不能使用子类。

子类 IS-A 基类。如果基类公开了一个方法,那么子类也必须公开一个方法。

Java 或 C++ 没有出路。我猜在 C# 中也是如此。

于 2012-10-08T11:30:25.990 回答
5

为什么我们不能降低能见度,但可以增加它?

假设有可能降低能见度。然后看下面的代码:

class Super {
    public void method() {
        // ...
    }
}

class Sub extends Super {
    @Override
    protected void method() {
        // ...
    }
}

假设您在另一个包中有另一个类,您在其中使用这些类:

Super a = new Sub();

// Should this be allowed or not?
a.method();

要检查是否允许方法调用,编译器会查看您调用它的变量的类型。变量的类型aSuper。但是实际a引用的对象是 a Sub,而方法是protected,所以你会说不应该允许从包外不相关的类调用方法。为了解决这种奇怪的情况,禁止使覆盖的方法不那么明显。

请注意,反过来(使方法更可见)不会导致同样的问题。

于 2012-10-08T11:32:21.287 回答
3

由于Java允许超类引用指向子类对象..所以,限制不应该从compile-time增加到runtime..

让我们通过一个例子来看看: -

public class B {
    public void meth() {

    }
}

class A extends B {
    private void meth() {  // Decrease visibility.

    }
}

现在,您创建一个类对象A并为其分配类的引用B..让我们看看如何:-

B obj = new A();  // Perfectly valid.

obj.meth();  // Compiler only checks the reference class..
             // Since meth() method is public in class B, Compiler allows this..
             // But at runtime JVM - Crashes..

现在,由于compiler只检查引用变量的类型,并检查该类(B类)中方法的可见性,而不检查obj引用的是哪种对象reference。所以,它不担心那..在运行时留给JVM来解决适当的方法..

但是在运行时,JVM实际上会尝试调用meth类的方法,A因为对象属于A类。但是,现在会发生什么...... BooooOOMM ---> JVM崩溃......因为meth方法是私有的class A......

这就是为什么不允许降低能见度的原因。

于 2012-10-08T11:33:52.213 回答