Java.lang.Object的受保护方法如何受到子类的保护?

12
关键字protected授予相同包中的类和子类的访问权限(http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html)。
现在,每个类都有java.lang.Object作为超类(http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html)。
因此,我得出结论,即使它们是protected,每个类也可以访问java.lang.Object的方法。
看下面这个例子:
public class Testclass {
  public Object getOne() throws CloneNotSupportedException {
    return this.clone();
  }
  public Object getTwo() throws CloneNotSupportedException {
    return ((Object) this).clone();
  }
}
getOne()编译良好,而getTwo()则给出以下错误:
Testclass.java:6: clone() has protected access in java.lang.Object
        return ((Object) this).clone();
我既不理解getTwo()为什么不能编译,也不知道与getOne()相比(关于对java.lang.Object成员的访问)有何区别。

很难相信当我看到ClonableObject.clone()时,我也有同样的困惑 - 谢谢。 - wulfgarpro
3个回答

16

如果你要访问不同包中的受保护成员,那么你只能通过编译时类型为自己的类或子类来引用它。这里的“自己的”类指的是包含代码的类。同时,你自己的类也必须是最初声明该方法的类型的子类。

以下是一个示例;假设Base在与所有其他类不同的包中:

package first;
public class Base
{
    protected void Foo() {}
}

// Yes, each class is really in its own file normally - but treat
// all the classes below as being in package "second"

package second;
public class Child extends Base
{
    public void OtherMethod(Object x)
    {
        ((Base) x).Foo(); // Invalid: Base is not Child or subclass
        ((Child) x).Foo(); // Valid: Child is Child
        ((GrandChild) x).Foo(); // Valid: GrandChild is subclass of Child
        ((OtherChild) x).Foo(); // Invalid: OtherChild is not Child or subclass
    }
}

public class GrandChild extends Child {}
public class OtherChild extends Base {}

换言之,它让你访问“与你相似的对象”的受保护成员。 详细信息请参见Java语言规范的6.6.2章节
一个对象的protected成员或构造函数只能被实现该对象的代码访问,而且只有在声明该成员的类中或其子类中才能访问。如果是通过限定名称Q.Id访问,则必须满足表达式Q的类型是SS的子类;如果是通过字段访问表达式E.Id或方法调用表达式E.Id(. . .)访问,则必须满足E的类型是SS的子类。

3
请注意参考文献中的内容,类可以访问同一包中任何类的受保护成员。 - Michael Myers
((OtherChild) x).Foo(); 这不应该是无效的吗? - fasih.rana
@fasih:是的,糟糕了。解释是正确的,但结果错误 :) - Jon Skeet
只需要更清楚一点关于 ((OtherChild) x).Foo(); 的问题,这是无效的,因为对 Foo() 的调用在类 OtherChild 之外,而不是因为那里提到的注释 - 无效:OtherChild 不是 Child 或子类。我可以从 class OtherChild() 中调用 Foo(); - ajmartin
@ajmartin:不,正是因为那个评论——在Child内部,你只能在Child实例或其子类上调用foo() - Jon Skeet
显示剩余4条评论

5
当你说“((Object) this).clone()”时,你通过它的超类Object访问了自己的对象。你对Object执行了扩展转换。然后代码尝试在Object上调用clone方法。
但是,正如你所注意到的,clone是一个受保护的方法,这意味着只有当你的对象在java.lang相同的包中时,它才能访问OBJECT的克隆方法。
当你说this.clone时,你的类继承了Object,因此可以通过受保护的类修饰符直接覆盖或使用clone,因为它继承了Object。但是,这并没有改变Object的实现。
通过说((Object) yourObject),你得到的是只能通过Object类访问的东西。只有java.lang包之外的Object类的公共方法是可访问的,因此你会得到编译时异常,因为编译器知道这一点。
通过说this.clone(),你正在调用你的对象的克隆方法,它是通过从Object继承而来,并且现在能够被调用,因为它成为你的自定义子类的一部分。

你把protected和package-protected搞混了。 - Michael Myers
我不这么认为:“当你说this.clone时,你的类继承了Object类,因此通过继承具有访问和重写或直接使用clone方法的受保护类修饰符的能力。” - Jon Skeet
我对这个陈述进行了挑剔:“但是,正如你所指出的,克隆是一种受保护的方法,这意味着只有在你的对象与java.lang在同一个包中时,它才能访问OBJECT的克隆方法。” 表面上看起来似乎是错误的。但让我再考虑一下... - Michael Myers
好的,我现在明白了。+1 让我动了脑筋。 - Michael Myers
抱歉,mmyers。我的意思是你的类无法访问Object的克隆方法,但它可以通过成为Object的子类来使用或覆盖它:只有在同一包中,即java.lang中,才能访问Object的实现(即使用Object.clone()而不是yourclass.clone())。 - MetroidFan2002

1

区别在于如何访问Object.clone()。只有通过子类或同一包中的类访问时,才能访问clone。在getOne()示例中,您正在调用this.clone()。这显然满足从子类内部访问的要求。

但是,在getTwo()中,您正在访问Object.clone()而不是TestClass.clone()。为了使其工作,您必须具有对Object的包级访问权限,但您没有,因此会出现错误。


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