Skip to main content

Why an outer Java class can’t be private or protected

As soon as we try to use private or protected keyword while declaring an outer class compiler gives a compilation error saying “Illegal modifier for the class your_class_name; only public, abstract & final are permitted”.

Here in this article, we are going to study why we are not allowed to use these keywords while declaring outer classes. But before understating the reason behind this we need to understand Java access specifiers and their use cases. There is total 4 access specifier in Java mentioned below in the order of their accessibility.

  1. private: anything (field, class, method, interface etc.) defined using private keyword is only accessible inside the entity (class or package or interface) in which it is defined. 
  2. default: only accessible inside the same package and it is also known as package-private (No modifiers needed).
  3. protected: only accessible inside the same package plus outside the package within child classes through inheritance only. 
  4. public: can be accessed from anywhere.

Why an outer class can not be private

As we already know a field defined in a class using private keyword can only be accessible within the same class and is not visible to outside world.

So what will happen if we will define a class private, that class will only be accessible within the entity in which it is defined which in our case is its package?

Let’s consider below example of class A

package com.example;
class A {
private int a = 10;

// We can access a private field by creating object of same class inside the same class
// But realy no body creates object of a class inside the same class
public void usePrivateField(){
A objA = new A();
System.out.println(objA.a);
}
}

Field ‘a’ is declared as private inside ‘A’ class and because of it ‘a’ field becomes private to class ‘A' and can only be accessed within ‘A’. Now let’s assume we are allowed to declare class ‘A’ as private, so in this case class ‘A’ will become private to package ‘com.example’ and will not be accessible from outside of the package.

So defining private access to the class will make it accessible inside the same package which default keyword already do for us, Therefore there is no benefit of defining a class private it will only make things ambiguous.

Why an outer class can not be protected

Access specifier protected is sometimes got confused with the default keyword, for some programmers it becomes hard to identify the exact difference between default and protected accesses. But it is very clear as mentioned below

    default → only accessible within the same package.
    protected → accessible within the same package as well as outside of the package in child classes  through inheritance only.

Let’s consider below example of class A

package com.example;
public class A {
protected int a = 10;
}

And suppose there is one more class in another package

package com.experiment;
public class B extends A {

// Outside of the package protected field can be accessed through inheritance
public void printUsingInheritance() {
System.out.println(a);
}

// In child class we can access protected field through instantiation of child class
// But should we do that ? .... No
public void printUsingInstantiation() {
B b = new B();
System.out.println(b.a);

// But not through instantiation of the class which contains the protected field
A a = new A();
System.out.println(a.a); // Compilation error “The field A.a is not visible”
}
}

And suppose there is one more class in the same package

package com.experiment;
public class C {

// We can not access protected field outside of the child class through instantiation
public void printUsingInstantiation() {
B b = new B();
System.out.println(b.a); // Compilation error “The field B.a is not visible”
}
}

And if same class 'C' extends 'B', Then again we will be able to access 'a' the same way we are able to access it in B class

package com.experiment;
public class C extends B {

// outside of the package protected field can only be accessed through inheritance
public void printUsingInheritance() {
System.out.println(a);
}

// In child class we can access protected field through instantiation as well
public void printUsingInstantiation() {
C c = new C();
System.out.println(c.a);
}
}

Because ‘a’ field is protected, we can access it in any way we want to inside the package but outside of the package ‘com.example’ it is only accessible through inheritance and because class ‘B’ is extending class ‘A’ we can use field ‘a’ inside class ‘B’ only as we are doing in printUsingInheritance() method. And we can not use ‘a’ outside of class ‘B’ without inheriting 'B' in another class.

Field ‘a’ will be accessible inside class B in through inheritance only but if we try to create an instance of class ‘B’ in ‘B’ class and then try to access ‘a’ from that instance we will able to use it in a similar manner we can use a private variable in the same class by instantiation of same class.

So If we are allowed to make a class protected then we can access it inside the package very easily but for accessing that class outside of the package we first need to extend that entity in which this class is defined which is again is its package.

And since a package can not be extended (can be imported) defining a class protected will again make it similar to defining it as default which we can already do. So again there is no benefit of defining a class protected.

Please feel free to reach me if you face any problem in understanding it or found any problem in the article.

Comments

  1. Dear Naresh:
    I typed in your code and did not get a compilation error in printUsingInstantiation. Furthermore, it was necessary to import A, otherwise it wouldn't compile. Am I doing something wrong?
    Here's the code, along with the main()


    package com.psi.outer;

    public class A {
    protected int a = 10;

    }
    ===================================
    package com.psi.outer.experiment;

    import com.psi.outer.A;

    public class B extends A {
    //a = 20;
    // Outside of the package protected fields can only be accessed through inheritance
    public void printUsingInheritance() {
    System.out.println(a);
    }

    // Even in a child class we can't access protected fields through instantiation
    public void printUsingInstantiation() {
    B b = new B();
    System.out.println(b.a);
    }
    }
    ==============================================================
    package com.psi.outer.experiment;

    public class M {

    public static void main(String[] args) {

    B bb = new B();
    System.out.println("PrintUsingInheritance: ");
    bb.printUsingInheritance();
    System.out.println("");
    System.out.println("PrintUsingInstantiation: ");
    bb.printUsingInstantiation();
    }

    }

    ReplyDelete
    Replies
    1. Sorry Jorge, It was a mistake, I have updated the article.

      We can access it within the same class through instantiation as well but not outside of the class.

      Delete

Post a Comment

Popular posts from this blog

Why Single Java Source File Can Not Have More Than One public class

According to Java standards and common practices we should declare every class in its own source file. And even if we declare multiple classes in the single source file (.java) still each class will have its own class file after compilation. But the fact is that we can declare more than one class in a single source file with below constraints, Each source file should contain only one public class and the name of that public class should be similar to the name of the source file. If you are declaring the main method in your source file then main should lie in that public class If there is no public class in the source file then main method can lie in any class and we can give any name to the source file. If you are not following 1st constraint then you will receive a compilation error saying “ The public type A must be defined in its own file ”.  While if you are not following the second constraint you will receive an error “ Error: Could not find or load main class User ” after exec

AutoWiring Spring Beans Into Classes Not Managed By Spring Like JPA Entity Listeners

In my previous article JPA Auditing: Persisting Audit Logs Automatically using EntityListeners , I have discussed how we can use Spring Data JPA automate Auditing and automatically create audit logs or history records and update CreatedBy, CreatedDate, LastModifiedBy, LastModifiedDate properties. So in order to save history records for our File entity, we were trying to auto-wire EntityManager inside our FileEntityListener class and we have come to know that we can not do this. We can not inject any Spring-managed bean in the EntityListener because EntityListeners are instantiated by JPA before Spring inject anything into it. EntityListeners are not managed Spring so Spring cannot inject any Spring-managed bean e.g. EntityManager in the EntityListeners. And this case is not just with EntityListeners, you can not auto wire any Spring-managed bean into another class (i.e. utility classes) which is not managed by Spring. Because it is a very common problem and can also arise with other c

How Does JVM Handle Method Overloading and Overriding Internally

In my previous article Everything About Method Overloading Vs Method Overriding , I have discussed method overloading and overriding, their rules and differences. In this article, we will see How Does JVM Handle Method Overloading And Overriding Internally, how JVM identifies which method should get called. Let’s take the example of parent class  Mammal and a child  Human classes from our previous blog to understand it more clearly. public class OverridingInternalExample { private static class Mammal { public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); } } private static class Human extends Mammal { @Override public void speak() { System.out.println("Hello"); } // Valid overload of speak public void speak(String language) { if (language.equals("Hindi")) System.out.println("Namaste"); else System.out.println("Hello"); } @