You are currently viewing Stranger things about Java: the protected Modifier

Stranger things about Java: the protected Modifier

What you are reading is one in a series of articles titled “Stranger things in Java”, inspired by the contents of my book “Java for Aliens”. These articles are dedicated to insights of the Java language. Deepening the topics we use every day, will allow us to master the Java coding even in the strangest scenario.

The protected modifier is a rarely-used access specifier. It is applicable to variables, methods and constructors, but not to Java types (classes, interfaces, enumerations, annotations and records), unless they are nested in other types. In this post, we will avoid talking about nested types and we will focus on this modifier referring mainly to class members (variables and methods) to make the discussion simpler. In particular, we will make some important observations regarding protected, which is often used without the necessary awareness. In addition, we will also digress on the Singleton pattern to add extensibility to it.

 

Definition

All Java programmers know how to use the public and private modifiers, as they are used daily in programming. The protected modifier, on the other hand, is used much less frequently, and when used it is not uncommon for it to be used inappropriately, sometimes because its correct definition is ignored.

When applied to a member of a class, the protected modifier defines the highest degree of accessibility after that defined by the public modifier. In fact, a protected member will be accessible within the same package (via the dot operator), but will also be inherited in all subclasses of the class in which it is defined, even if not belonging to the same package.

 

Package Scope

If we talk about member visibility, using the protected modifier or using no modifier is the same thing. In fact, we call package scope (or default scope) when we do not use any modifier to declare a class member, that will be accessible in all types defined in the same package.

For example, consider the ProtectedClass class which declares a protected variable and a protected method:

package com.claudiodesio.blog.staj;

public class ProtectedClass {
    
    protected int protectedVariable;
    
    protected void protectedMethod() {
        System.out.println("Protected method invoked");
    }
}

Furthermore, let’s consider the following ProtectedClassSamePackage class belonging to the same package:

package com.claudiodesio.blog.staj;

public class ProtectedClassSamePackage {
    
    public void methodUsingProtectedMembers() {
        var protectedClass = new ProtectedClass();
        protectedClass.protectedMethod();
        System.out.println(protectedClass.protectedVariable);
    }
}

This class will compile without problems, even if it explicitly uses the members declared protected in the superclass via the dot operator as if they were declared public.

Instead, if we try to use protected members in the same way in a class belonging to a different package like the following:

package com.claudiodesio.blog.staj.other;

import com.claudiodesio.blog.staj.ProtectedClass;

public class ProtectedClassOtherPackage {
    
    public void methodUsingProtectedMembers() {
        var protectedClass = new ProtectedClass();
        protectedClass.protectedMethod();
        System.out.println(protectedClass.protectedVariable);
    }
}

we will get the following compile-time errors:

ProtectedClassOtherPackage.java:9: error: protectedMethod() has protected access in ProtectedClass
        protectedClass.protectedMethod();
                      ^
ProtectedClassOtherPackage.java:10: error: protectedVariable has protected access in ProtectedClass
        System.out.println(protectedClass.protectedVariable);
                                         ^
ProtectedClassOtherPackage.java uses preview language features.
2 errors

just because it is in a different package.

We would have obtained the same result even if the members of the class ProtectedClass had declared them with package scope, that is, we had not used any modifier.

 

Inheritance

However, declaring a protected member is important when in addition to wanting to limit the scope to the package it belongs to, we also want it to be inherited in subclasses even if external to the package. So, using protected makes sense only when we want to use inheritance. In fact, the subclasses inherit members declared protected in the superclass, even if these subclasses belong to packages different from the package to which the superclass belongs.

For example, the following subclass of the ProtectedClass class:

package com.claudiodesio.blog.staj;

public class ProtectedSubclassSamePackage extends ProtectedClass {
    
    public void methodUsingProtectedMembers() {
        var protectedClass = new ProtectedClass();
        protectedMethod();
        System.out.println(protectedVariable);
    }
}

belongs to the same package as the superclass, and can be compiled without errors.

But the same goes for a subclass that belongs to a different package like the following:

package com.claudiodesio.blog.staj.other;

import com.claudiodesio.blog.staj.ProtectedClass;

public class ProtectedSubclassOtherPackage extends ProtectedClass {

    public void methodUsingProtectedMembers() {
        var protectedClass = new ProtectedClass();
        protectedMethod();
        System.out.println(protectedVariable);
    }
}

 

The Stranger Thing

Unfortunately, another class that is in a package other than com.claudiodesio.blog.staj, will not be able to access the protected members inherited from the ProtectedClass superclass, because the scope of these methods is limited to the package where they were declared. For example, compiling the following class:

package com.claudiodesio.blog.staj.other;

public class StrangerThingsAboutProtectedTest {
    public static void main(String args[]) {
        ProtectedSubclassOtherPackage psop = 
            new ProtectedSubclassOtherPackage();
        psop.metodoProtected();
    }
}

will result in an error whose description leaves no doubt:

StrangerThingsAboutProtectedTest.java:7: error: protectedMethod() has protected access in ProtectedClass
        psop.protectedMethod();
            ^
1 error 

So, although StrangerThingsAboutProtectedTest is in the same package as the ProtectedSubclassOtherPackage class, it cannot invoke its protected method, because the scope of that method is limited to the com.claudiodesio.blog.staj package where it was declared.

Clearly, if the StrangerThingsAboutProtectedTest class belonged to the com.claudiodesio.blog.staj package and correctly imported the ProtectedSubclassOtherPackage class, the compilation would be successful.

 

Workaround

In this case, to fix the problem we can override the protected method protectedMethod in the ProtectedSubclassOtherPackage subclass, for example, in the following way:

package com.claudiodesio.blog.staj.other;

import com.claudiodesio.blog.staj.ProtectedClass;

public class ProtectedSubclassOtherPackage extends ProtectedClass {

    public void methodUsingProtectedMembers() {
        var protectedClass = new ProtectedClass();
        protectedMethod();
        System.out.println(protectedVariable);
    }
    
    @Override
    protected void protectedMethod() {
        super.protectedMethod();
    }
}

Now the StrangerThingsAboutProtectedTest class can compile and run correctly. In fact, the protectedMethod method is redefined by the ProtectedSubclassOtherPackage class, belonging to the com.claudiodesio.blog.staj.other package where the StrangerThingsAboutProtectedTest class is also located.

Note that for the overriding rules, the protectedMethod method in the ProtectedSubclassOtherPackage subclass could also be declared public.

 

And what about variables?

The same problem also arises with protected variables. In fact, if the method of the StrangerThingsAboutProtectedTest class tried to access the protectedVariable variable:

public static void main(String args[]) {
    ProtectedSubclassOtherPackage psop = 
        new ProtectedSubclassOtherPackage();
    psop.protectedVariable = 1;
}

we would get the following error:

StrangerThingsAboutProtectedTest.java:8: error: protectedVariable has 
protected access in ProtectedClass
        psop.protectedVariable = 1;
            ^
1 error 

Also in this case, in order to compile correctly, we could rewrite the variable in the subclass:

// omitted package and import declaration
public class ProtectedSubclassOtherPackage extends ProtectedClass {

    protected int protectedVariable;
// rest of the code omitted

An absolutely inelegant solution. On the other hand, as we will see in the next sections, the protected modifier can be very useful when applied to methods, constructors and in some cases to constants. It is much less useful when applied to variables.

 

Protected Constructors

By declaring a protected constructor, we can make a class instantiable only within a certain package, but at the same time extendable with subclasses also belonging to different packages. An interesting example is the one related to the famous Singleton design pattern. A typical implementation in Java involves the declaration of a private constructor. Actually, in the famous book that first formalized this pattern: “Design Patterns: Elements of Reusable Object-Oriented Software“, it was proposed to declare the constructor as protected, to allow the extension of the Singleton class (see Figure 1).

Figure 1: the code example written in C ++ proposed in the Design Patterns book.

The proposed example was written in C ++ (Java did not exist at the time), where the protected modifier is defined in a simpler and more natural way. In fact, since the concept of package does not exist, the use of the protected modifier in C ++ guarantees private scope and support for inheritance.

To support the inheritance of a Singleton class in Java, on the other hand, declaring the constructor as protected is not enough, it is also necessary to include this class within a dedicated package, that is, it does not contain other classes. If not, the other classes could instantiate the singleton class violating the pattern philosophy. Having said this, the following example represents an extensible singleton:

package com.claudiodesio.blog.staj.singleton;

public class SingletonExample {
    private static SingletonExample instance;

    protected SingletonExample () {
    }

    public static SingletonExample getInstance() {
        if (instance == null) {
            synchronized (SingletonExample.class) {
                instance = new SingletonExample();
            }
        }
        return instance;
    }
    // other code omitted
}

Provided that the com.claudiodesio.blog.staj.singleton package does not contain any other classes.

 

Protected Variables

But if the use of protected is primarily intended for methods and constructors, declaring a protected variable is most of the time unnecessary or even wrong.

In fact, a common mistake among newbies, is to think that in order to use a certain variable in a subclass, it is necessary to declare it as protected, when it is already sufficient to have a mutator (set) and an accessor (get) methods inherited in the subclasses. Furthermore, declaring an instance variable as protected means making it directly accessible to all classes belonging to the same package, and except in rare cases, this is not the level of encapsulation desired. Therefore, if we consider the Item class:

public class Item {
    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

In its Book subclass, we can safely set and get the id variable. In fact, even though we didn’t inherit it, we still inherited the setId and getId methods. For example, we could implement the Book subclass in the following way:

public class Book extends Item {
    public Book (int id, String title){
        setId(id);
        setTitle(title);
    }
    private String title;

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
}

Note that we used the setId method within the constructor. So, it is not necessary to declare the variable id as protected in the Item superclass, and thus break the encapsulation contract.

 

Conclusion

In this article we have explored a basic topic, such as the protected modifier, and in particular its relationship with inheritance. This modifier can be useful when applied to methods and constructors, but much less for encapsulated variables. Since it is one of those topics that are learned in the early stages of learning the language, and since it is a fairly rare-used modifier, some programmers overlook the subtleties of its definition, and in some cases it is used inappropriately.

 

Author Notes

This article is based on some sections of the chapters 5 and 6 of my English book “Java for Aliens“.

 

Leave a Reply