class One {
void foo() { }
}
class Two extends One {
private void foo() { /* more code here */ }
}
为什么上面的代码片段错了?
答案 0 :(得分:11)
我将尝试将其他答案中的想法合并为一个答案。
首先,让我们来看看代码中发生了什么。
查看代码
One
类有一个包私有foo
方法:
class One {
// The lack of an access modifier means the method is package-private.
void foo() { }
}
Two
类的子类One
和foo
方法被覆盖,但具有访问修饰符private
。
class Two extends One {
// The "private" modifier is added in this class.
private void foo() { /* more code here */ }
}
问题
Java语言不允许子类降低子类中方法,字段或类的可见性,因此Two
类降低foo
方法的可见性是不合法的。 / p>
为什么降低可见性是一个问题?
考虑我们想要使用One
类的情况:
class AnotherClass {
public void someMethod() {
One obj = new One();
obj.foo(); // This is perfectly valid.
}
}
在这里,调用foo
实例上的One
方法是有效的。 (假设AnotherClass
类与One
类位于同一个包中。)
现在,如果我们要实例化Two
对象并将其放在obj
类型的One
变量中,该怎么办?
class AnotherClass {
public void someMethod() {
One obj = new Two();
obj.foo(); // Wait a second, here...
}
}
Two.foo
方法是私有的,但One.foo
方法允许访问该方法。我们在这里遇到了问题。
因此,在考虑继承时允许降低可见性没有多大意义。
<强>链接强>
答案 1 :(得分:1)
此代码的问题在于,如果它是合法的,如果您通过private
基类间接访问它,Java将无法尊重foo
的{{1}}修饰符。例如,如果我要写
One
然后我们遇到麻烦,因为我们会间接调用One obj = new Two();
obj.foo();
private
foo
方法,因为当编译器检查行Two
时它看起来像在obj.foo()
确定One
是否可访问,而不是foo
。原因是编译器不能总是告诉Two
可能指向的是什么 - 例如,如果我写了类似
obj
然后编译器无法知道One obj = Math.random() < 0.5? new One() : new Two();
obj.foo();
是指向obj
还是One
。因此,在检查访问说明符时,它会推迟到Two
。如果确实允许我们在One
中标记foo
私有,则编译器会错误地允许我们通过类型为Two
的{{1}}来调用它,绕过保证只有对象本身可以调用obj
方法。
答案 2 :(得分:1)
给出的答案为您提供技术解释,说明为什么不能逐一扩展。我想让你理解为什么由于面向对象的模式而不是因为语言本身而无法实现这一点。
通常,类One是类的一般定义,具有访问器,方法和外部世界。扩展此类的子阶段必须为外部世界提供相同的访问者。两个扩展在你的例子中,一个意思是,两个提供与外部世界相同的访问者,就像One一样。如果您要更改One的访问者的可见性,则外部世界将无法再访问您的类,因为他们习惯使用类型为One的对象。
答案 3 :(得分:0)
它会破坏多态性。
如果你有两个实例存储在一个Two变量中,那么foo就不能被调用了。但是如果你在One变量中存储一个Two实例,你只能知道一个。但是,One有一个公共foo,可以称之为。这将是一个不一致的,并且会非常奇怪。
答案 4 :(得分:0)
因为继承是是的关系。任何人都可以通过对Two
的引用来引用One
的实例:
One v = new Two();
如果在v引用上调用foo方法,程序会怎么做?你打破了One的公共合同,这保证了One的每个实例都有一个(这里是受包保护的)foo方法。这就是编译器禁止它的原因。