Stop using public access modifier in Java

TLDR; Make the implementations package-private rather than public to restrict developers from depending on those implementations rather than interfaces at the compiler-level.Photo by JC Gellidon on UnsplashMost of the time in Java, the default access m…


This content originally appeared on Level Up Coding - Medium and was authored by Görkem Gök

TLDR; Make the implementations package-private rather than public to restrict developers from depending on those implementations rather than interfaces at the compiler-level.
Photo by JC Gellidon on Unsplash

Most of the time in Java, the default access modifier in IDEs for a newly created class or method is public while it is private for fields. When we add a field to a class in Java, it is very common to make it private. However, we make classes public by default.

First, let’s remember why we use a private access modifier for fields.

Yes! For the sake of encapsulation. We want to limit access by hiding some of the data to provide a consistent interface without exposing how it is implemented (Encapsulation in Wikipedia).

We, as developers, (should) hide other things to create evolvable software. We hide kernel calls behind OS API or SQLs behind ORMs. We also create abstraction layers by using interfaces in Java to hide the implementations.

But wait! Is it really hidden if we use a public access modifier in the implementation class?

Let’s say we have an id generator component and its implementation:

// Note that they are in the same package
package com.gorkemgok.component.generator;

public interface IdGenerator {
String generate();
}
// Note that they are in the same package
package com.gorkemgok.component.generator;

public class UUIDGenerator implements IdGenerator {

public String generate() {
return UUID.randomUUID().toString();
}

}

Ideally, other user classes should depend on the IdGenerator interface. The implementation (UUIDGenerator) should be unknown to the user classes and injected into those classes (ideally by DI Framework such as Spring, Guice, etc).

Let’s say SomeOtherClass is any user class that depends on IdGenerator:

//Note that this is in a different package than IdGenerator and its implementation
package com.gorkemgok.example.repository;

public class SomeOtherClass{

private IdGenerator idGenerator;

...

}

As shown in the code, SomeOtherClass depends on the IdGenerator interface.

If SomeOtherClass depends on the implementation class (UUIDGenerator) that would break the abstraction and that would be an anti-pattern. (Interface-based programming). However, it is very easy to violate this principle in Java because it is a public class. A developer can create a class that depends on the implementation (UUIDGeenrator) and it is also very likely to miss that in code reviews. We can/should restrict depending on the implementation by making it package private.

Let’s remove the public access modifier from the class:

package com.gorkemgok.component.generator;

//We made it package private by deleting the public access modifier
class UUIDGenerator implements IdGenerator {

public String generate() {
return UUID.randomUUID().toString();
}

}

Voila! With one little touch, our code is more resistant to future code smells. No class outside of the package com.gorkemgok.example.generator can depend on the implementation class.

Now it is literally hidden from other packages. If we keep our packages cohesive we can achieve very high abstraction for our implementations. You can think of interface classes as the APIs of our packages. The user can only see the API and knows nothing about the details so the details can change without breaking the user classes.

Problem with package-private interfaces

We can also create package-private interfaces but there is a problem!

Let’s say we create another implementation of the IdGenerator interface and need a random character generator for that.

package com.gorkemgok.component.generator;

class RandomIdGenerator implements IdGenerator {

private final RandomChar randomChar;

...

}
package com.gorkemgok.component.generator;

//This interface is package-private
//because it meant to be used only within IdGenerator component
interface RandomChar {

char random();

}

In the case that we make an interface package-private, the implementations of that interface should be in the same package. However, if we do that the RandomIdGenerator (the user class) can depend on the implementation of RandomChar. This would break the encapsulation that we want to achieve.

We can not implement a package-private interface in a nested package either (such as package com.gorkemgok.companent.generator.random) because the implementation and the RandomChar interface would be in different packages.

Unfortunately, we don’t have access modifiers for nested packages in Java. We don’t have semantic nested packages in Java, either. Semantically, every package is at the same level.

The best way to hide implementations from users is to create a package for each component/service/layer that we want to encapsulate and put public interface and package-private implementations in it.

So we would create package com.gorkemgok.companent.generator.random and put the public RandomChar interface and the package-private implementations in this package.

To achieve more complex architecture containing nested packages you can force developers to obey specific rules by using the ArchUnit test library (link). It is not a compiler-level restriction but it makes your unit tests fail in case of any architecture violation.

Dependency Injection

Let’s go back to package-private implementations.

We don’t want any other class to depend on the implementation but this results in a class that is not instantiable outside of that package. Unfortunately, Java doesn’t give many options for that. You need a factory class inside the same package unless you use a dependency injection framework like Spring or Guice.

Spring Framework

Spring can scan and register package-private classes as beans. Add a stereotype annotation (@Component, @Service, etc. ) to your class, and if you are using @SpringBootApplication or @ComponentScan annotation Spring would take care of the rest.

Guice

In Guice, you need to create your Guice module inside the same package as you do with a factory class.

Summary

We know and apply encapsulation in classes very well but why not use it in packages? It is easier to change the parts that are hidden behind the abstractions.

The more hidden, the more evolvable software.

Forcing developers to use abstractions instead of implementations at the compiler level will prevent your codebase to get rotten in time.

Trying to achieve this abstraction will also make you see the design flaws better. When you need to depend on an implementation you will stop and try to figure out why you need that dependency that you shouldn’t need.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


Stop using public access modifier in Java was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Görkem Gök


Print Share Comment Cite Upload Translate Updates
APA

Görkem Gök | Sciencx (2023-01-17T12:20:06+00:00) Stop using public access modifier in Java. Retrieved from https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/

MLA
" » Stop using public access modifier in Java." Görkem Gök | Sciencx - Tuesday January 17, 2023, https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/
HARVARD
Görkem Gök | Sciencx Tuesday January 17, 2023 » Stop using public access modifier in Java., viewed ,<https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/>
VANCOUVER
Görkem Gök | Sciencx - » Stop using public access modifier in Java. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/
CHICAGO
" » Stop using public access modifier in Java." Görkem Gök | Sciencx - Accessed . https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/
IEEE
" » Stop using public access modifier in Java." Görkem Gök | Sciencx [Online]. Available: https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/. [Accessed: ]
rf:citation
» Stop using public access modifier in Java | Görkem Gök | Sciencx | https://www.scien.cx/2023/01/17/stop-using-public-access-modifier-in-java/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.