1

在 Java 中,我们不能在继承类时指定 private、protected 或 public。例如,下面的代码在 Java 中是不可能的。为什么?

class A {
    public void display() {
        //Some implementation of display logic.
    }
}

class B extends **protected** A
{
    public void show() {
         display();
         //some more logic.
    }
}

B obj = new B();
obj.show();

我不想拥有 obj.display() 并且需要对外界隐藏它的实现。在 C++ 中,我们可以有基于访问修饰符的继承,但在 java 中没有。我的问题是,如果我们想隐藏实现,我们该如何实现?

为什么在 Java 中做出这种架构决策以在继承期间删除访问修饰符,使用它有什么害处?

4

4 回答 4

2

请注意,您不能降低方法或字段的可见性,因为这会导致将子类传递给需要超类的方法时出现问题。但是,您可以增加它。

让我们看一个例子:

public static void displayFromA(A a){
    a.display();
}

由于BextendsA我们可以传递B给这个。如果B'display(方法的可见性降低,此方法将不起作用。这将打破 Liskov 替换原则的前提,正式地,如果q(x)是一个方法调用能力的真值,display(那么q(x)x作为 A 的实例成立,但不一定x是 A 的子类型。

于 2013-07-24T19:19:45.400 回答
1

C++ 提供了允许将物理继承用作类的实现细节的一部分的能力。

这部分 C++ 是Barbara Liskov 引入现在称为Liskov 替换原则之前设计的。为了满足 Liskov 替换原则,只要使用超类的实例,也可以使用子类的实例。在您的情况下,您可以将 B 传递给期望 A 的方法。

Java是在引入这一原则后设计的。在 Java 中,继承的物理机制仅用于旨在从其超类逻辑继承的类。要使用类的实现而不是其接口,可以使用委托。

来自 Stroustrup 的《The design and evolution of C++》:

“基类的私有/公共区别比实现继承与接口继承的争论早了大约五年 [Synder,1986][Liskov,1987]。如果你只想继承一个实现,你可以在 C++ 中使用私有派生。公共派生使派生类的用户可以访问基类提供的接口。私有派生为基类留下了实现细节;即使是基类的公共成员也无法访问,除非通过为派生类明确提供的接口。

于 2013-07-24T19:25:20.447 回答
0

防止访问底层 display() 的一种方法是覆盖它。例如,在您的 B 类中:

@Override
public void display() {
  throw new UnsupportedOperationException("Do not use B.display(), please use B.show() instead");
}

并且,在 B 中,例如在 show() 中,改为调用 super.display()。

但是,这将在运行时被捕获,而不是在编译期间。添加一些 javadocs 并希望他们阅读它们。:-)

我不知道有一种方法可以添加像 @NeverCallMe 注释这样的东西,它会在编译时被捕获。有谁知道这个技巧吗?

(补充)顺便说一句,我同意 Java 的设计者和其他答案,这是你通常不应该为正确的 OO-ness、Liskov 和所有这些做的事情。但这是一种有点笨拙的方式来做你想做的事。

于 2013-07-24T19:47:08.300 回答
0

如果您想对所有人隐藏该信息,则没有理由使 B 类与 A 类具有“IS-A”关系。

如果您只想重用 A 类中的代码,您可以让 B 类包装 A 的一个实例并委托给它,如下所示:

class B{
    //this is our delegate to A
    private A myA = new A();

    public void show() {
         myA.display();
         //some more logic.
    }
}

B obj = new B();
obj.show();
于 2013-07-24T19:51:58.217 回答