为什么子类的方法可以更容易访问?

3
一个快速的例子。
class A {
    protected int foo(int x){
        return x;   
    }
}

class B extends A {
    public int foo(int x){
        return x*x; 
    }
}

这在Java中是允许的,而且没有任何问题。但是假设在另一个包中你声明了...
A b = new B();
int z = b.foo(5);

因为 A foo() 是受保护的,所以这样做是行不通的。但是为什么要允许在子类中拥有更可访问的方法呢?这样做有帮助的情况吗?


3
为什么要禁止它?子类的私有性并不能真正得到保证:如果你想要,你可以创建另一个方法来导出该值。 - Denys Séguret
1
这就是著名的里氏替换原则:子类可以更加宽松地允许调用方法,因为它至少和父类一样宽松。 - Andy Turner
3个回答

3
一般情况下,子类可以向其父类继承的接口添加方法。将方法变得更加可访问实际上是在增加接口。
但是为什么要允许子类拥有更多可访问的方法呢?
因为对于持有子类引用的代码来说,这样做非常有用。
是否存在这种情况有帮助的案例呢?
一个很好的例子是Object.clone(),它是一种受保护的方法。所有Java类都是直接或间接地继承自Object。支持克隆的子类可以选择将此方法公开。
Foo foo1 = new Foo();
Foo foo2 = foo1.clone(); // Sometimes you're holding a subclass reference.

关于“克隆”的一般示例还不错,1+,但通常该方法应该在“Cloneable”中;在“Object”中拥有它是一个可怕的想法,在我看来。 - Eugene
@Eugene - 是的,无疑地,Cloneable接口应该包含一个clone()方法。[在Joshua Bloch的Effective Java中有更多讨论:“明智地覆盖clone”。]作为另一件事,提供一个支持逐字段复制的默认实现是有用的。受保护的Object成员为子类提供了一个默认实现,可供选择性使用。无论Object方法是否被称为clone()或其他名称,子类都可以选择提供公共的clone()方法。 - Andy Thomas
clone() 的设计不好,因为它依赖于子类中 Object.clone() 的访问修饰符扩大。因此,我认为 clone() 不是使用访问修饰符扩大的好例子。相反,它更是一个我们不应该使用访问修饰符扩大的好例子。 - davidxxx
@davidxxx - 你在这里和你的回答中都断言了,但并没有证明它有害的影响。你所断言的会导致某些“只有在运行时才会失败”的情况似乎并不正确。 - Andy Thomas

1
因为类的设计应该允许你将子类视为其超类的实例。我认为这被称为“is-a”关系(即“B is-a A”,但反之不成立)。我不知道如何简洁明了地解释这一点。
在你的例子中,所有的B实例也是A实例(请注意这可能是一个过度简化,但我认为对于这个例子来说足够了)。A只保证具有其自身定义中的公共方法。B只是恰好使其中一个受保护的方法变为public,但假设你有另一个名为C的类不这样做。那么,你可以将BC都转换为A,但如果你被允许调用A上的受保护方法,因为B使它变为公共的,那么当你传递C时会出错。
在另一个包中,您将 B 强制转换为 A,因此您的对象实际上采用了 A 的接口,因此您只能使用由 A 给出的接口。JVM 能否费力地注意到 1) A 实际上是 B,以及 2) B 使 foo 公开,因此应该可以调用 foo?我想它可能会这样做,但是要在 JVM 中实现这一点需要多少工作量,并且容易出错吗?保持简单。如果类型为 A,则接口为 A。如果类型为 B,则接口为 AB 包含的其他内容。

0

如果你用B替换掉A,第二个示例也可以编译通过。 这就是重点:B可以被用在任何A能用的地方,但也可以添加其他功能(例如将方法设置为公共)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接