为什么我不能在包外使用受保护的构造函数?

41

为什么我无法在包之外使用受保护的构造函数来运行这段代码:

package code;
public class Example{
    protected Example(){}
    ...
}

Check.java

package test;
public class Check extends Example {
  void m1() {
     Example ex=new Example(); //compilation error
  }
}
  1. 尽管我扩展了这个类,为什么还是会出现错误?请解释一下。

编辑:

编译错误:

The constructor Example() is not visible


了解编译错误在诊断问题时非常有用... - Jonny Henly
看这个 https://dev59.com/2G435IYBdhLWcg3w4Uas 你可以在子类的构造函数中使用超类的受保护构造函数,但不能在其他任何地方实例化超类。 - redge
基本上是同样的原因,你不能这样做 public class Example {protected int i;} /* in another package: */ public class Check extends Example {void m1(Example ex) {ex.i = 2;}} - user253751
重复问题的选择很不幸,因为这两个问题虽然相关,但是它们是不同的,而另一个问题根本没有回答这个问题。 - Konrad Rudolph
3个回答

14

protected通常意味着只能被子类或同一包中的类访问。然而,以下是JLS中构造函数的规定:

6.6.2.2. 对受保护的构造函数的合格访问

设C是声明受保护构造函数的类,S是在其声明中使用受保护构造函数的最内层类。则:

如果访问是由超类构造函数调用super(...)或限定超类构造函数调用E.super(...)引起的,其中E是主表达式,则允许访问。

如果访问是由匿名类实例创建表达式new C(...){...}或限定匿名类实例创建表达式E.new C(...){...}引起的,其中E是主表达式,则允许访问。

如果访问是由简单类实例创建表达式new C(...)或限定类实例创建表达式E.new C(...)引起的,其中E是主表达式,或方法引用表达式C :: new,其中C是ClassType,则不允许访问。受保护的构造函数只能通过类实例创建表达式(不声明匿名类)或方法引用表达式从定义它的包中访问。

例如,这段代码无法编译:

public class Example extends Exception {

    void method() {
        Exception e = new Exception("Hello", null, false, false);
    }
}

但这确实如此。

public class Example extends Exception {

    Example() {
        super("Hello", null, false, false);
    }
}

这也是如此

public class Example {

    void method() {
        Exception e = new Exception("Hello", null, false, false) {};
    }
}

规则很清楚,但我不太理解背后的原因!


1
学到新东西了 ;) - kittu
1
@kittu 我也是。我没有意识到会有那样奇怪的限制。 - Paul Boddington

6

protected修饰符仅在包内和包外的子类中使用。当您使用Example ex=new Example();创建对象时,默认情况下将调用父类构造函数。

由于父类构造函数受保护,因此您会收到编译时错误。根据JSL 6.6.2.2,您需要按照示例2中所示调用受保护的构造函数。

package Super;

public class SuperConstructorCall {

    protected SuperConstructorCall() {
    }

}

package Child;

import Super.SuperConstructorCall;

public class ChildCall extends SuperConstructorCall
{

    public static void main(String[] args) {

        SuperConstructorCall s = new SuperConstructorCall(); // Compile time error saying SuperConstructorCall() has protected access in SuperConstructorCall
    }
}

符合JLS 6.6.2.2的示例2:

package Super;

    public class SuperConstructorCall {

    protected SuperConstructorCall() {
    }

}

package Child;

import Super.SuperConstructorCall;

public class ChildCall extends SuperConstructorCall
{

    public static void main(String[] args) {

        SuperConstructorCall s = new SuperConstructorCall(){}; // This will work as the access is by an anonymous class instance creation expression 
    }
}

1
不是的,protected 可以跨包使用,只需要继承该类即可。default 作用域仅限于同一包内。**protected 修饰符指定成员只能在其自己的包内访问(与包私有相同),并且可以被另一个包中其类的子类访问。** - TheLostMind
3
回答仍然不够有说服力,我知道它会调用父类的构造函数Example(),而这个构造函数是受保护的,但我们知道如果我们扩展该类,则可以在包外使用protected修饰符,那么为什么在构造函数的情况下这样做不起作用呢? - Abhilash28
1
为什么需要独立调用超类的构造函数?如果它们不是默认访问或私有访问,您可以直接访问超类的方法。 - Ya Wang

3
事实上,您已经在使用Example的受保护构造函数,因为Check有一个隐式构造函数和隐式的Example构造函数调用:
public Check() {
    super();
}

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