Using Protected Access Modifier Correctly in Java

The attributes or methods with protected access modifier in parent class are inherited in subclass, hence the subclass can access them. When the subclass or parent class object is created in a class that reside in different package, we can not access the protected attributes from the object reference. So protected access modifier works like private as far access from user-classes (i.e. where we instantiate an object of that class) are concern. It works like public as far inheritance is concern, as its accessible in subclass code. That is why it comes between private and public access.

So here comes a fundamental question, should we make our instance attributes protected if we want to make them available to subclasses?

From good software design perspective, the straight answer is BIG NO, we should not make the instance attributes protected even if we want to make them inheritable, why? For the same reason as discussed in previous sections i.e. it breaks the encapsulation principle, hence it would cause the subclasses to directly depend on data/state instead of behavior (read Encapsulation topic, if you do not understand this reason). So what we should do here?

We should be very clear what exactly we want to achieve. If we want to make instance attributes inheritable without violating the encapsulation principle, we can make the attributes private and provide public get/set methods for that attribute, so the subclass we use those get/set methods read/update the inherited attribute values. But you know, if we make the get/set public, the user-classes (e.g. StudentTest class above, or any other class that creates the instance of subclass) would also access/update the inherited attribute.

In previous section we discussed, making the instance attribute protected is not right way. So the only reason to use protected access is with methods. When you want to define some fields that only subclasses can access and not the user class, you should declare that field as private in parent class, and provide protected get/set methods. These protected methods would be visible to subclasses but the user-class would not be able to access/update the value of inherited attribute. See below the code section:

package com.bitspedia.java.inheritance;

public class Person {
    private int id;

    public Person(int id) {
       setId(id);
    }    
    
    protected int getId() {
        return id;
    }

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

The Student class would access the inherited attributes using protected get/set methods, see toString() method below in which we have accessed inherited attribute via getter method. You may initialize then parent class attributes using constructor of parent class instead of setId(..) method. If you need to change the value of id after the object is created, then you can call setId() method directly, as its protected in parent class.

package com.bitspedia.java.inheritance;

public class Student  extends Person {
    private String courseName;
    
    public Student(int id, String courseName) {
        super(id);        
        setCourseName(courseName);
    }
    
    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }    

    @Override
    public String toString() {
        return "Student{" + "courseName=" + courseName + ", id=" + getId() + "}";
    }
}

In this way, we made id available to subclasses via protected get/set methods and make it hidden from user-classes by making the get/set methods protected. But please note, if the user-class (the class that instantiate the parent or subclass object) resides in same package then it can access the protected methods too. In other words, it works just like default access for classes in same package.

Comments