Back to Blog
Java

Java Protected Members Inheritance

java protected members inheritance: Understand how Java's protected access modifier works across packages and subclasses, with clear examples and common pitfalls.

access modifiersinheritanceprotected keywordsubclass accessJava OOPpackage-private
Diagram showing a subclass accessing a protected member through its own type but blocked through a superclass reference

java protected members inheritance requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

In Java, the protected access modifier sits between private and public, but its exact behavior in an inheritance hierarchy is often misunderstood. For working developers, the practical question is: when a subclass inherits a protected member, what can it access, and on which instances? The answer depends on whether the subclass is in the same package as the superclass, and the rules differ in a way that trips up many Java developers.

This article covers the precise rules of java protected members inheritance, with concrete examples that show where access is allowed and where the compiler rejects your code. By the end, you'll know how to design your class hierarchies to avoid access-related surprises.

The Exact Rule for Protected Member Access

The Java Language Specification defines protected access with two clauses. A protected member of a superclass is accessible within the subclass, but the access is subject to two conditions:

  1. The access must occur in code within the subclass body (directly or via inheritance).
  2. The access must be on an expression whose type is the subclass itself or a subclass of that subclass.

This second condition only applies when the access occurs across packages. In the same package, protected members behave like package-private (default) members, so any class in the package can access them on any instance of the superclass or subclass.

To put it simply: a subclass in a different package can only access protected members through references of its own type (or subtypes), not through a reference of the superclass type.

Same-Package Access: The Simple Case

When the subclass is in the same package as the superclass, protected members are accessible just like package-private members. There is no restriction on the type of the reference variable.

Consider this example:

package com.example.animals; public class Animal { protected String name; protected void makeSound() { System.out.println("Generic animal sound"); } }

And a subclass in the same package:

package com.example.animals; public class Dog extends Animal { public void describe() { // Direct access to protected field on this instance System.out.println(name); // Access on another Animal reference Animal other = new Animal(); other.makeSound(); // Allowed: same package // Access on another Dog reference Dog buddy = new Dog(); buddy.name = "Buddy"; } }

Here, Dog can access the name field and call makeSound() on any Animal reference because Dog and Animal share the same package. This is the simpler scenario, and it's often what developers expect from the protected modifier.

Cross-Package Access: The Conditioned Rule

When the subclass is in a different package from the superclass, the access rules tighten. The subclass may access a protected member only through a reference whose type is the subclass (or a subclass of that subclass). Access through a superclass reference is forbidden.

Consider the same Animal class but a subclass in a different package:

package com.example.pets; import com.example.animals.Animal; public class Cat extends Animal { public void act() { // Direct access to protected field on this instance System.out.println(name); // OK } public void compare(Cat other) { System.out.println(other.name); // OK: reference type is Cat } public void inspect(Animal someAnimal) { // Compile error: cannot access protected member via Animal reference // System.out.println(someAnimal.name); } }

If Cat tries to access name through an Animal reference, the compiler rejects it. The rationale hinges on type safety: were it allowed, a Cat could potentially access protected members of an Animal that is not actually a Cat, breaking encapsulation.

This rule also applies to methods. Calling a protected method through a superclass reference from a different-package subclass results in a compile-time error.

Why the Rule Exists: Type Safety and Encapsulation

The cross-package restriction prevents a subclass from accessing protected members of arbitrary superclass instances. Without it, a class could iterate over a collection of Animal objects and invoke protected methods on all of them, even those not of the subclass type. That would widen the surface of the protected API beyond its intended visibility.

By limiting access to references of the subclass type, Java ensures that a subclass can only interact with protected members of objects it is actually related to through inheritance. This keeps the protected modifier as a true "inheritance-friendly" access level, not a backdoor to package-private-like behavior across packages.

The Overriding Connection: Protected Visibility

One important interaction is how protected members affect method overriding. If a superclass declares a protected method, a subclass can override it and change the access modifier to protected or public. Reducing visibility to private or package-private is not allowed because that would break substitutability.

package com.example.finance; public class Account { protected double getBalance() { return 0.0; } }
package com.example.banking; import com.example.finance.Account; public class SavingsAccount extends Account { @Override protected double getBalance() { return 1000.0; // OK: narrowing to private would fail } }

Keeping the overriding method at least as accessible as the original ensures that code written against the superclass still works when a subclass instance is used.

Common Pitfalls in Real-World Code

Developers frequently hit the cross-package restriction when refactoring or extending frameworks. A typical mistake is to have a helper method in a subclass that tries to access a protected field on a superclass reference passed as a parameter. The compiler complains, and the fix is often to cast the reference to the subclass type or to use a public or package-private accessor.

Another pitfall is confusing protected with package-private when designing APIs. If a member should be accessible to all classes in the same package but not outside, using protected is wrong because subclasses outside the package can still access it (albeit under the restriction). Use the default (no modifier) access for package-private behavior.

Access Modifier Decision Guide

Choosing the right modifier depends on your design goals:

ModifierSame packageSubclass (different package)World
privateNoNoNo
package-private (default)YesNoNo
protectedYesYes (with type restriction)No
publicYesYesYes

Use package-private when the member should be visible only to collaborating classes in the same package. Use protected when subclass hook points are intended and cross-package extension is expected, but be mindful of the type restriction. In practice, many developers default to protected for fields that subclasses need, but exposing getter/setter methods is often a better long-term choice for maintainability.

Final Consideration: The Type Restriction in Practice

When designing a class hierarchy that spans packages, remember that the protected member is only accessible through the subclass's own type. This affects how you write code that processes collections of superclass instances. If a subclass needs to access protected members of multiple objects, it must ensure those objects are of the subclass type, which often requires explicit casting or a more specific collection type.

For example, if your SavingsAccount class needs to iterate over a list of Account objects and call a protected method on each, it cannot do so directly. You either change the method visibility or restructure the logic. The restriction may feel limiting, but it preserves the integrity of the protected API.

Understanding these rules helps you avoid compile errors and design APIs that accurately reflect your intended access boundaries. When in doubt, prefer the least permissive modifier that meets your requirements, and document the intended access level for future maintainers.

java protected members inheritance: Practical Usage and Code | RYUSLOG DEV