当尝试在其他包中的子类中使用受保护的构造函数时,Eclipse会显示错误。

3

大家好,我是 Stackoverflow 社区的新成员 :-]

首先感谢你们提供的非常有用的建议和更正。由于我是法国人,请原谅我的英语不完美。

这是我的问题:我目前正在学习Java编程语言,并且想测试一些继承的东西。如果我理解正确,声明为protected的字段可以被与声明受保护字段的类在同一个包中的类访问,以及所有它的子类,无论它们是否在同一个包中。

因此,我创建了这4个类来测试这个问题。我有一个名为“package1”的包,其中包含A和C类。我还有一个名为“package2”的第二个包,其中包含A2和C类,其中A2扩展A。两个C类具有完全相同的代码,只是它们所在的包发生了变化。它们不扩展A。

在A类中,我声明了一些具有不同访问权限的成员,特别是使用protected可见性声明的构造函数。以下是四个类的代码。

package1,A类:

package package1;

public class A {

    public int a;
    protected int b;
    private int c;
    int d;

    protected static int h = 30;

    protected void aff(){
        System.out.println(h);
    }

    protected A(){
        a = 1;
        b = 2;
        c = 3;
        d = 4;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

包 1,类 C:

package package1;

public class C {

    public C(){
        super();
    }

    public void app(){
        A obj = new A(); //////// OK
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub


        A obj = new A(); //////// OK
        obj.aff(); //////// OK

        System.out.println(obj.a);

    }

}

包2,类A2(继承自A):

package package2;
import package1.A;

public class A2 extends A{

    public int x;

    A2(){
        super();
    }


    public void app(){
        A obj = new A(); //////// ERROR
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub


        A obj = new A(); //////// ERROR

        A2 obj2 = new A2();
        obj2.aff(); //////// OK



    }

}

包2,类C:

package package2;
import package1.A;

public class C {


    public C(){
        super();
    }

    public void app(){

        A obj = new A(); //////// ERROR
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub


        A obj = new A(); //////// ERROR
        obj.aff(); //////// ERROR

        System.out.println(obj.a);

    }

}

对于 package2 中的 C 类,代码 A obj = new A(); 报错了,但对于 package1 中的 C 类,情况并非如此。这是正确的,因为构造函数被声明为 protected,而 package2 中的 C 不是 A 的子类,而 package1 中的 C 是 A 的子类。至此,我明白了。
我的问题在于 A2 类中的代码 A obj = new A();:无论在哪里写它,都会报错 The constructor A() is not visible... 因为 A 类的构造函数被声明为 protected,所以为什么我不能在 A2 类中实例化 A 类型的对象?
当我将 A 构造函数声明为 public 时,它可以正常工作。此外,如果我按原样将 A2 类放在 package1 中,它也可以正常工作。似乎只有当 A 类构造函数被声明为 protected 时,在其子类中实例化 A 对象才可能是与其位于同一包中的子类。
但是,正如您所看到的,如果我首先实例化一个 A2 对象,然后调用 A 类的 protected aff() 方法,则它可以正常工作,受到保护的规则得到了遵守。
有人能解释这个错误吗?在其子类中实例化超类对象时,如果超类构造函数声明为 protected,那么这个子类是否总是位于与其超类相同的包中?如果是这样,为什么会这样?还是这与构造函数不被子类继承有关?但我想不出为什么会这样...
非常感谢您抽出时间来阅读和回答 :-]

你的问题可以更简洁明了。请参考最小化、完整性和可验证性 - Andreas
1
“protected”很有趣:) 似乎只有从子类构造函数中才能调用受保护的构造函数。 - ZhongYu
这个缺乏规范的阅读如何成为一个好问题。 - itwasntme
对于我的问题长度,我感到抱歉,我想尽可能清晰地表达给大家。下次我会尝试正确匹配“最小化、完整性和可验证性”的要求 :-] @itwasntme:实际上,我在Java规范中找到了一些解释,但是它很难理解...它使用了对我的技能来说过于通用和复杂的示例,以及过于复杂的英语技术语言。我感到困惑,不明白他们想要说什么,这也是我在这里发布我的问题的原因。阅读规范并不总是意味着您清楚地理解它。 - NatsuDragon
2个回答

3
请参考Java语言规范
一个对象的protected成员或构造函数只能被负责该对象实现的代码在其所在的包外部访问。
你的类A2不负责在new A()调用中实现A。
也就是说,它不负责实现A的实例,但是它负责实现A2的实例。

非常感谢您的快速回复!我认为我已经理解了如果A2在同一个包中为什么它能够工作,以及如果A2在A的包外为什么它不能工作。实际上,在这种情况下,只有A2的构造函数可以通过super()或隐式调用访问A的构造函数。 - NatsuDragon

3

这很有趣,让我来总结一下。请参见JLS#6.6.1

protected可以修饰类的构造函数或成员。

“成员”包括字段/方法(静态/实例)、嵌套的类/接口(静态/内部)。

class A {
    protected int f
    protected void m(){}
    protected class X{}
    protected interface Y{}

首先,要访问受保护的构造函数/成员,封闭类(例如 A)必须是可访问的。假设这是情况,则--

--在包内--

受保护的构造函数或成员可以在同一包中的任何地方访问。

--在包外--

受保护的构造函数仅在子类构造函数中可访问,可以作为super()调用或匿名类实例化。

受保护的静态字段/方法、嵌套类/接口可以在子类主体中的任何地方访问。


受保护的实例字段/方法更加复杂--

  • 受保护的“m”在类A中定义
  • 在类B(A的包外)中访问obj.m
  • obj的类型是C

只有当B是A的子类,并且C是B的子类或C是B时,才允许访问obj.m

super.m始终被允许;但是,JLS如何构建问题并不清楚。似乎应该将访问视为this.m,因此允许访问。


非常感谢您详细的回复!如果我理解正确的话,当我将A2放在与A相同的包中,并尝试在A2的主体中实例化一个A对象时,它能正常工作的原因是从访问权限的角度来看,A2首先被视为与A相同包中的一个类,然后才是其子类。 - NatsuDragon
@NatsuDragon protected 意味着与“包私有”(当没有关键字时)拥有相同的权限,因此将 A2 放在与 A 相同的包中允许访问,因为遵循 包私有 访问规则。由 protected 授予的附加权限不起作用。 - Andreas
@Andreas 我现在明白了。因此,protected 授予的附加权限仅涉及外部包中的子类。在那里,只有子类构造函数才能调用protected 超类构造函数。关于超类的protected 实例成员,它们必须通过子类实例(或其自身的子类)访问,而protected 静态成员可以在子类的任何位置访问。感谢澄清 :-] - NatsuDragon
1
@NatsuDragon 正确。请注意,即使不访问自己的实例(this),也会授予权限。在equals(obj)方法中经常看到这种情况,其中从A2代码允许使用this.a == ((A2)obj).a - Andreas

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