2

Normally it is said that when we are using protected for a field in a class then its subclass cannot access it using a reference of the Base Class given that the subclass is in a different package . That is true . But I found that it behaves differently when a static keyword is added with the field . It becomes accessible . How is it possible . Is any one having the answer .

package com.car;

public class Car {

    static protected int carNo=10;

}


package com.bmw;
import com.car.*;

public class BMW extends Car {

    public static void main(String[] args) {
        //Its accessible here
        System.out.println(new Car().carNo);
    }
}
John Topley
  • 113,588
  • 46
  • 195
  • 237
xpioneer
  • 3,075
  • 2
  • 16
  • 18

5 Answers5

3

6.6.2.1. Access to a protected Member

Let C be the class in which a protected member is declared. Access is permitted only within the body of a subclass S of C.

In addition, if Id denotes an instance field or instance method, then:

If the access is by a qualified name Q.Id, where Q is an ExpressionName, then the access is permitted if and only if the type of the expression Q is S or a subclass of S.

If the access is by a field access expression E.Id, where E is a Primary expression, or by a method invocation expression E.Id(. . .), where E is a Primary expression, then the access is permitted if and only if the type of E is S or a subclass of S.

Source : https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.2.1

see

public class BMW extends Car {
    public static void main(String[] args) {
        System.out.println(new BMW().carNo);
    }
}

is valid because new BMW() is a subclass of Car, even being in a different package.

public class BMW extends Car {
    public static void main(String[] args) {
        System.out.println(new Car().carNo);
    }
}

is not valid because new Car() is not a subclass of Car, and it's being called in a different package. (see Java: Is a class a subclass of itself? for a discussion if a class is subclass of itself)

Now, if carNo is static, this is legal

System.out.println(new Car().carNo);

However, the right syntax here would be

System.out.println(Car.carNo);

because carNo is not an instance field, since it's static. In fact, even this will work from inside BMW

System.out.println(carNo);

because

Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared

as stated at https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.2

Community
  • 1
  • 1
Leo
  • 751
  • 4
  • 29
1

The main method is in BMW, that is a subclass of Car. Thus, it has access to the protected variable.

The reason it was not visible before was because static methods, like main, cannot access non-static variables. Once both criterias are fullfilled, the main method can access it.

Erik Nyström
  • 537
  • 4
  • 9
  • That doesn't answer the actual question. – zubergu Jan 13 '16 at 13:58
  • @zubergu Might have been a little bit muddy, yes. Edited it to be a little more explaining. – Erik Nyström Jan 13 '16 at 14:03
  • @AnkitDeshpande attempt to access carNo is non-static so it shouldn't matter if carNo is static or not. Or is it accessed from static or non-static context for that matter. – zubergu Jan 13 '16 at 14:11
  • I attempt, it matter, you cann't access to new Car().carNo from no-static method of BMW, only if you write new BMW().carNo – Slava Vedenin Jan 13 '16 at 14:14
  • we cannot call class variables from static methods/ static context, irrespective of protected/public inheritance/ no inheritance scenario – Ankit Deshpande Jan 13 '16 at 14:17
  • I just want add one more thing . If we make the carNo as non static , we can't access it in BMW using an instance of Car (no matter if it is accessed in static or non static context) . But if we make it 'static' ,the rule of the game changes. Please give some suggestion on this. – xpioneer Jan 13 '16 at 17:01
  • the variable is not public. you cannot directly access it. check my ans. i have made an edit to it. you can do it using getter and setter methods – Ankit Deshpande Jan 13 '16 at 18:33
1
class Car {
   protected int a = 9;
}

class BMW extends Car{
    public static void main(String[] args) {
      int b = a; // cannot make a static reference to a non static field warning error shown by eclipse
    }
}

Two ways to remove it: either make a static

class Car {
   protected static int a = 9;
}

class BMW extends Car{
    public static void main(String[] args) {
    int b = a; // cannot make a static reference to a non static field
    }
}

or call it outside main in a non static method, main being static cannot call to class variables

class Car {
   protected static int a = 9;
}

class BMW extends Car{
    public void m() {
    int b = a; 
    }
    public static void main(String[] args) {

    }
}

You are mixing two concepts here:
1) accessing static variables from non-static context
2) protected access modifier
in java you can access protected members through inheritance or only within the same package.

Try accessing noCar here:

class Car{
    int noCar = 9;
    public static void main(String[] args) {
      int b = noCar; // cannot make a static reference to a non static field warning error shown by eclipse
    }
}

EDIT: considering packages

package com.bmw;

import com.car.*;

public class BMW extends Car {

    public static void main(String[] args) {
       System.out.println(new BMW().carNo);
       Car car = new Car();
       // Car has no idea that BMW is the child class
       // and since it is not public we cannot access it directly
        //can be accessed like this
        car.getCarNo();
       // you can do this because BMW has the variable carNo because of it extending Car
       BMW bmw = new BMW();
       int a = bmw.carNo;
    }
}

package com.car;

public class Car {

    protected int carNo=10;
    public int getCarNo() {
        return carNo;
    }

    public void setCarNo(int carNo) {
        this.carNo = carNo;
    }

}
Ankit Deshpande
  • 3,476
  • 1
  • 29
  • 42
  • If you use protected int a = 9; instead of protected static int a = 9; in your lase example you see Error:(15, 37) java: a has protected access in com.car.Car. Question not how fix, but why this happen. – Slava Vedenin Jan 13 '16 at 14:24
  • @ViacheslavVedenin thanks for pointing this out. I did it in a single package. I have added the explanation in the edit. – Ankit Deshpande Jan 13 '16 at 14:25
  • I'm sorry but your answer by skipping the packaging part is completely irrelevant to the problem. – zubergu Jan 13 '16 at 14:29
0

The reason being the keyword "static".

Static associates the variable with the class and the not the instance. Since the class is public , all it's static variables will also be public i.e. all the variables will be accessible from other classes.

Also BMW extendsCar. Hence it will always be visible to BMW.

Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
0

Yes, that is weird. In order to shed some light on this behaviour, it may be helpful, first of all, to recap how the protected modifier works in the absence of the static keyword, and why it works the way it does.

If class T declares a protected member m, then T and any class belonging to the same package as T can access the member, i.e. can say t.m; the type of the reference (t) must be T or a subclass of T. Additionally, any subclass U of T outside T's package can say t.m; in this case, the type of t must be U or a subclass of U.

The final part of this statement contains an important restriction. Its motivation is succintly explained in section 3.5 of The Java Programming Language (fourth edition) by Arnold, Gosling and Holmes:

The reasoning behind the restriction is this: Each subclass inherits the contract of the superclass and expands that contract in some way. Suppose that one subclass, as part of its expanded contract, places constraints on the values of protected members of the superclass. If a different subclass could access the protected members of objects of the first subclass then it could manipulate them in a way that would break the first subclass's contract -- and this should not be permissible.

Let's try to better understand this explanation by putting your Car and BMW classes to work. The following is a modified version of Car. I replaced the carNo field with an equally protected, but non-static, color field. The class also declares an obvious public getter/setter pair.

package com.car;

import java.awt.Color;

public class Car {

    protected Color color;

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }
}

Here is the BMW class, which at the moment does nothing but inherit Car's members.

package com.bmw;

import com.car.Car;

public class BMW extends Car {
}

Finally, let's add another subclass of Car.

package com.ferrari;

import com.car.Car;
import java.awt.Color;

public class Ferrari extends Car {

    public Ferrari() {
        color = Color.RED;
    }

    @Override
    public void setColor(Color color) {
        log("Nope. I'm proud of my color.");
    }

    ...
}

As you can see, in our application Ferrari objects show an exclusive preference for a certain color (which is almost true in the real world, too). The color field is set in the constructor, and made read-only by a straightforward override of setColor(). Notice, by the way, that direct access to the color protected member is permitted here because the reference (an implicit this) is of the right type, that of the accessing subclass (Ferrari plays the role of U in the above description).

Now, suppose that BMW objects want to demonstrate their superiority over other cars, so they ask the class's programmer to be enhanced by means of a bold overtake() method. The programmer obliges.

...

public class BMW extends Car {

    public void overtake(Car car) {
        log("Wow! Become green with envy!");
        car.setColor(Color.GREEN);
    }

    ...
}

But, reading the application logs, the programmer soon discovers that, while this works fine with other cars, Ferrari objects stubbornly resist any insolence. He then, urged by BMW objects to find a solution, tries to by-pass the setColor() method...

...

public class BMW extends Car {

    public void overtake(Car car) {
        log("Wow! Become green with envy!");
        car.color = Color.GREEN;    // <-
    }

    ...
}

... which is exactly what we can't do in Java. The Ferrari subclass's expanded contract places a constraint on the value of the color protected member. If the BMW subclass could directly access the color field through a Car (or Ferrari) reference, it would be able to break that contract. Java does not allow this.

So, this is why the protected modifier behaves the way it does when applied to non-static members. With protected static members, things change altogether. If the color field were static, any method inside BMW could directly access it. In your code, the BMW class accesses the carNo field without a hitch.

In the example above, the Ferrari class can restrict the possible values of the color field by overriding the instance setColor() method, which effectively amounts to changing, without violating, the superclass's contract.

Now, Java is, by design, a class-based object-oriented language, which does not have a concept of class object in the same sense as, for example, Objective-C. In Objective-C, classes are, literally, objects, and class methods (analogous, but non identical, to Java static methods) are, so to speak, instance methods of the class object -- with all the consequences of this fact: in particular, they can be overridden and used as polymorphic operations, the array class method in NSArray and NSMutableArray being an obvious example.

In Java, there is no class object -- an instance of java.lang.Class is by no means the same thing as an Objective-C class object. Static methods are, in essence, functions with an associated namespace. Most importantly, they can be inherited, but can't be overridden -- only hidden, just like static and non-static fields. (By the way, this means that invoking a static method is more efficient than calling an instance method, because not only its form, but also its implementation can be chosen at compile-time.)

But, and this is the end of the story, if static members cannot be overridden, they cannot really change the superclass's contract either. And, if they cannot change the superclass's contract, a subclass cannot break the contract of a different subclass by only accessing the latter's static members. If we recall that avoiding this kind of violations was precisely the reason of the restriction concerning protected non-static members, we can now understand why the designers of Java ended up lifting that restriction for protected static members. Once again, we can find a concise allusion to this line of thought in a short passage from section 3.5 of The Java Programming Language:

Protected static members can be accessed in any extended class... This is allowed because a subclass can't modify the contract of its static members because it can only hide them, not override them -- hence, there is no danger of another class violating that contract.

Alberto Doda
  • 281
  • 2
  • 6