2010-08-22 585 views
13

我想了解下面的示例中发生了什么(通过子类从包外部访问受保护成员的位置)。Java:跨包的受保护访问

我知道包外的类,子类只能通过继承来看到受保护的成员。有两个包:package1package2

  1. package1ProtectedClass.java

    package org.test.package1; 
    
    public class ProtectedClass { 
    
        protected void foo() { 
         System.out.println("foo"); 
        } 
    } 
    
  2. package2ExtendsprotectedClass.java

    package org.test.package2; 
    
    import org.test.package1.ProtectedClass; 
    
    public class ExtendsprotectedClass extends ProtectedClass { 
    
        public void boo() { 
         foo(); // This works, 
           // since protected method is visible through inheritance 
        } 
    
        public static void main(String[] args) { 
         ExtendsprotectedClass epc = new ExtendsprotectedClass(); 
         epc.foo(); // Why is this working? 
            // Since it is accessed through a reference, 
            // foo() should not be visible, right? 
        } 
    } 
    
  3. package2UsesExtendedClass.java

    package org.test.package2; 
    
    public class UsesExtendedClass { 
    
        public static void main(String[] args) { 
         ExtendsprotectedClass epc = new ExtendsprotectedClass(); 
         epc.foo(); // CompilationError: 
            // The method foo() from the type ProtectedClass 
            // is not visible 
        } 
    } 
    

据了解,在ExtendsprotectedClassboo()方法可以访问foo(),由于受保护成员可以通过继承才可以访问。

我的问题是,为什么是foo()方法通过在ExtendsprotectedClassmain()方法的引用访问通过UsesExtendedClassepc参考访问时,将无法正常工作时工作正常?

回答

12

允许ExtendsprotectedClass类中的代码通过ExtendsprotectedClass类型的引用访问ProtectedClass的受保护成员。从JLS section 6.6.2

的对象的被保护的成员或构造可以从它仅由代码中声明的封装,它负责该对象的执行外部访问。

令C是其中一个保护构件米被声明的类。如果Id表示实例字段或实例方法,则:

  • 如果访问是通过限定名Q.Id,其中Q是一个ExpressionName,则接入是允许的,当且仅当表达式Q的类型是S或S的一个子类[...]

UsesExtendedClass不承担一切的ExtendsprotectedClass实施因此最终通话失败。

编辑:这背后的推理是protected访问旨在帮助子类实现他们需要的功能,提供更多的访问超类的内部比通常可用。如果这可用于全部的代码,那么将该方法公开是非常接近的。基本上,子类是可信的,不会破坏封装;他们在自己类型的对象中获得更多的功能。公共API不应该公开这些细节,但受保护的API只是为了给子类提供更多机会。

+0

@Jon谢谢。我明白,子类的类成员可以访问受保护的成员(如'boo()'方法中所示)。但很想知道为什么它允许通过子类的引用来访问受保护的成员,**仅在子类方法中**。任何背后的理由? – JWhiz 2010-08-22 08:26:19

+0

@JWhiz:编辑... – 2010-08-22 08:39:05

+1

2)工作原理是因为受保护的方法是由它自己的类的指针访问的。这应该失败:
public static void ExtendsprotectedClass.main(String [] args){ ProtectedClass epc = new ExtendsprotectedClass(); // upcast
epc.foo(); //应该是编译错误?
} – 2010-08-22 08:52:31

1

我相信你已经回答了你自己的问题; UsesExtendedClass不从ProtectedClass继承,并且 - 根据定义 - “受保护”成员只能在声明/定义的类中或者在从声明或定义声明或定义的类继承的类中访问。

+2

'按定义 - “受保护的”成员只能在它们被声明/定义的类中访问。 我不完全同意这一点,因为受保护的成员可以从同一包中的其他类进行访问而无需继承。 – JWhiz 2010-08-22 08:22:21

2

它在第一种情况下工作,因为即使通过引用访问方法,它也是从同一个类中调用的。您甚至可以通过相同主要方法中的参考调用private方法ExtendsprotectedClass

+1

谢谢。现在我明白为什么它能够通过参考访问它。 '+ 1'。但是,不应该允许通过Object引用调用'private'方法吗?这是否违背了“私人”的目的?允许这样做的具体原因是什么? – JWhiz 2010-08-22 10:04:58

+1

@JWhiz java访问修饰符在类而不是实例级别工作。因为这个私有方法对于类是私有的而不是实例。如果私人在实例上工作,如果两个实例必须交互,则必须公开更多的变量和方法。这将通过在类的外部显示实现来破坏封装。 – josefx 2010-08-22 16:10:25