2

Possible Duplicate

The top answer given there states that the protected constructor can be called using super() but cannot be instantiated using new? Why so? And if that is the reason then what is the difference between package-private(no modifier) and protected constructor? The following code is not getting me any difference.

package pack1;

public class Rr {
    protected Rr() {
        System.out.println("Rello");
    }
}

package pack2;

class Tt extends pack1.Rr {
    Tt() {
        super(); //works 
        pack1.Rr r = new pack1.Rr(); //error
    }
}

class Uu {
    public static void main(String args[]) {
        Tt t = new Tt();
    }
}
Karla Carr
  • 21
  • 4
  • 2
    The access modifiers have no special meaning when written in front of constructors. Thus, their normal definition applies (see [here](https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html)). In short: the difference between `default` and `protected` is that `protected` grants access from child-classes. – Turing85 Sep 17 '18 at 07:12
  • 2
    "Why so?" => The top answer also references the relevant part of the Java language specification. – Seelenvirtuose Sep 17 '18 at 07:15
  • @Turing85 The special case here is that both accesses (`super()` and `new ...`) are from within a sub class. This is what the question is about. – Seelenvirtuose Sep 17 '18 at 07:16
  • Weirdly enough, I just tried it out, and the call is _only invalid if the two classes are in separate packages_. The explanation can be found in the duplicate question you link. – daniu Sep 17 '18 at 15:17

3 Answers3

1

A "default constructor" is a constructor that Java generates if you don't provide one yourself; it's always has the same access as the class itself and has no arguments. Ie when you define

class YourClass {}

the system makes it

class YourClass {
    YourClass() {} // default constructor
}

If you create a any other constructor - even a public one with no arguments - it is not a default constructor. The second code piece above would prevent the system from generating one, because you already declared it.

The same is true if you create a protected constructor. Compare:

public class Class1 {}

public class Class2 {
    protected Class2() {}
}

you can instantiate Class1, but not Class2:

// invokes autogenerated default constructor
Class1 c1 = new Class1(); 
// compile error: default constructor is not generated because you declared a protected one
Class2 c2 = new Class2();

As @Turing85 states in a comment, the protected modifier only means that the declared constructor can only be invoked within the proper access scope - from subclasses or classes in the same package.

For completeness' sake: you can call both super() and the constructor explicitly from a subclass:

class Class3 extends Class2() {
    public Class3() {
        // call to protected constructor of Class2
        super(); 

        // explicit call to constructor by creating new instance
        Class2 o = new Class2(); 
    }
}

EDIT: I just tried it out, and the last sample is only valid if the two classes are in the same package. I don't know and would like to know.

daniu
  • 14,137
  • 4
  • 32
  • 53
  • The special case here is that both accesses (`super()` and `new ...`) are from within a sub class. This is what the question is about. – Seelenvirtuose Sep 17 '18 at 07:16
  • are you sure about the default constructor being **always** `public`? It should have, as I remember, the same access as the class. – user85421 Sep 17 '18 at 08:14
  • 1
    my memories aren't that wrong: [JLS-8.8.9-100-A](https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#jls-8.8.9-100-A) – user85421 Sep 17 '18 at 08:18
  • @CarlosHeuberger Nice catch. – daniu Sep 17 '18 at 08:20
1

A protected method can be accessed via its sub-class objects.

Note, "sub-class" here is very important, which means "via a sub-class way", not "anywhere within a sub-class".

Let's look at an example.

Following are 2 classes:

package one;
public class A {
    protected A() {} // protected constructor
    public A(int i) {} // public constructor

    protected void foo() {} // protected method
}

package two;
import one.A;
public class B extends A {
    public void bar() {
        this.foo(); // correct

        A a = new A(0); // call A's public constructor to construct an A instance.
        a.foo(); // compiler error: the method foo from the type A is not visible.
    }
}

The first invocation is permitted, but the second not, why?

Like I said, the first one is accessing via "a sub-class way", in which this is A's sub-class instance; however the second just "within a sub-class" (here B is no meaning to A, it's just an ordinary class, so it can not access A's protected method).


Another example is Object.clone. This is a protected method of root class Object. You might likely say it can be accessed from any object created from any class, but the answer is capital "NO": only those codes which are using "sub-classes way" can access it.

The following illustrates this:

this.clone(); // ok

Object o = new Object();
o.clone(); // compiler error: the method clone from the type Object is not visible.
terry.qiao
  • 1,959
  • 2
  • 8
  • 10
  • I find your explanation valid! Could you explain this code to me then too? http://pasted.co/4d591cbf – Karla Carr Sep 17 '18 at 14:28
  • @KarlaCarr Don't mix `protected` and `private` up. `private` can only be accessed within itself, and in your code, `main` IS itself. – terry.qiao Sep 18 '18 at 00:15
  • You can read the implementation of method `equals` in many of classes (`String`, for example) to understand this. – terry.qiao Sep 18 '18 at 00:17
  • Refer to: http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/2660b127b407/src/share/classes/java/lang/String.java#l976 – terry.qiao Sep 18 '18 at 00:28
  • I'm creating an instance of it and then accessing it. Which shouldn't be valid. For example, right now this should work http://pasted.co/f098e7b1 but this one pasted.co/4d591cbf shouldn't work because class f is class f and when I instantiate it in main() then it treats it like any other ordinary class isn't? – Karla Carr Sep 18 '18 at 14:13
  • @KarlaCarr No, absolutely not. `private` and `protected` are different. You treat `private` as a lower privileged `protected`, and that is not appropriate. – terry.qiao Sep 19 '18 at 02:37
  • If a field or method is declared in a class, then it can be accessed at any place within this class, no matter which object it belongs to. In your example, object `ob` is an `f` instance, thus within `f` class, you can access it without limitation. Actually, the point is, within a class, you can access any field / method across any object, as long as that field / method is declared in this class, and the object derives from this class as well -- no matter the modifier of that field / method is. – terry.qiao Sep 19 '18 at 03:02
0

protected gives access to subclasses, in addition to the classes in the same package. When you call the constructor directly, e.g. new A() you can think of it as calling a static method. There is no instance of the class yet, thus inheritance rules cannot apply. Therefore, static access rules apply: if the constructor is protected, then it must be in the same package in order to be accessible.

Once you are inside a constructor code and you call super(), then you already have an instance, so if you are in a subclass and call the protected constructor it will work.

memo
  • 1,868
  • 11
  • 19
  • 1
    Unfortunately this is an incorrect answer. Refer to my answer. – terry.qiao Sep 17 '18 at 07:27
  • I don't see how exactly is my answer wrong. Nor how your answer is different than mine, other than using the non-standard terminology "via a sub-class way" – memo Sep 17 '18 at 08:31
  • Actually it's not about `static` or `instantiated`, it's about the way you access the method (constructor or ordinary method are in the same rule, but in your answer they are different). – terry.qiao Sep 17 '18 at 08:56
  • Yes, it is. Have a look at the invokespecial bytecode which is used to implement constructors (https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokespecial). See the notes, practically the method to be called (i.e. the constructor) is resolved statically – memo Sep 17 '18 at 09:23
  • I didn't see any sentence says constructors are resolved statically. However I found this (https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.9): "and they may be invoked only on uninitialized class instances", `uninitialized` here means `instantiated but not initialized yet`. – terry.qiao Sep 17 '18 at 09:43
  • It's in the notes: The difference between the invokespecial instruction and the invokevirtual instruction is that invokevirtual invokes a method based on the class of the object. In other words based on the actual class of the receiver object, what you refer to as "the subclass way". The invokespecial instruction resolves the method statically, at class loading time, similar to invokestatic. You don't have a receiver object yet when calling new A(), just the class A (slightly different at bytecode level, but you may assume it at Java language level for the sake of this discussion). – memo Sep 17 '18 at 09:52
  • operator `new` creates instance, while `constructor` (or `initialization method`) initializes the instance -- instantiation and initialization are separated by two steps. In other words, when we call constructors (or even before we call them), the instances are already created (by `new`). So the conclusion is obvious, constructors are absolutely not invoked statically. – terry.qiao Sep 17 '18 at 09:53
  • "the subclass way" is from programmers' view, after all, `protected` is used among base classes and sub classes. And second, you need to distinguish between "resolve" and "invoke". In Java, all methods (no matter they are static or not) are resolved statically, so it proves nothing. – terry.qiao Sep 17 '18 at 10:06
  • I mistook you were saying "resolved", not "invoked" in my previous post. So just at this point, you are right, but it's no help for our discussion. – terry.qiao Sep 17 '18 at 10:10