Encapsulation before Java 9
- Private – Encapsulates method/variable within class. Not accessible outside of class.
- Default – Encapsulates method/variable within package.
- Protected – Encapsulates method/variable within package & sub classes.
- Public – Accessible everywhere.
- Selectively public – What if we want method/variable accessible everywhere only within the project (API/library) but not to anyone else outside using this project as dependency? – No Option (Till Java 9)
Here is the problem explained with code.
Calculator.Java – We want others to use this class to do addition. Internally this class will use InternalAdditionProvider.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// package com.itsallbinary.calculator; import com.itsallbinary.internal.InternalAdditionProvider; public class Calculator { public int add(int a, int b) { System.out.println("Calculator - Starting calculation of addition of " + a + " " + b); return new InternalAdditionProvider().add(a, b); } } |
InternalAdditionProvider.java – This is internal class in internal package & we don’t want others to use this class directly except Calculator.java. Actually we want to enforce it.
1 2 3 4 5 6 7 8 9 10 11 |
// package com.itsallbinary.internal; public class InternalAdditionProvider { public int add(int a, int b) { System.out.println("InternalAdditionProvider - Performing internal addition of " + a + " " + b); return a + b; } } |
Before Java 9, we can use calculator-module jar/dependency in any project & can access Calculator.add() as well as InternalAdditionProvider.add() directly even thought author doesn’t expect it. Author can try to put everything in one package & try using package level encapsulation but that would take away freedom of logical structuring. WIth given package structure, there was no way to block it at compiler level.
So below program will compile & run just fine even though it is using InternalAdditionProvider.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// This is client class in different project which has calculator-module as dependency. package com.itsallbinary.client; import com.itsallbinary.calculator.Calculator; import com.itsallbinary.internal.InternalAdditionProvider; public class CalculatorClient { public static void main(String[] args) { System.out.println("CalculatorClient - Using Calculator class"); System.out.println("Output = " + new Calculator().add(1, 2)); // Use InternalAdditionProvider directly even thought not intended to be used. System.out.println("CalculatorClient - Using InternalAdditionProvider class"); System.out.println("Output = " + new InternalAdditionProvider().add(1, 1)); } } |
Output:
1 2 3 4 5 6 7 |
CalculatorClient - Using Calculator class Calculator - Starting calculation of addition of 1 2 InternalAdditionProvider - Performing internal addition of 1 2 Output = 3 CalculatorClient - Using InternalAdditionProvider class InternalAdditionProvider - Performing internal addition of 1 1 Output = 2 |
How Java 9 modules solve this problem?
With Java 9, author of calculator-module library can restrict the access to InternalAdditionProvider at compiler level itself & make sure no one uses it directly. To do this, author will have to add module-info.java
Below is the code for module-info.java
1 2 3 4 5 6 7 8 |
// Give a name to module. Base package name can be used. module com.itsallbinary.calculator { // Only export this package. // All other packages should not be exported i.e. // should not be allowed to outside users. exports com.itsallbinary.calculator; } |
As per module-info.java code, only com.itsallbinary.calculator is exported i.e. other packages won’t be exported & allowed outside. Now after doing this, lets see what happens to our client code which directly tries to use InternalAdditionProvider.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// This is client class in different project which has calculator-module as dependency. package com.itsallbinary.client; import com.itsallbinary.calculator.Calculator; import com.itsallbinary.internal.InternalAdditionProvider; public class CalculatorClient { public static void main(String[] args) { System.out.println("CalculatorClient - Using Calculator class"); System.out.println("Output = " + new Calculator().add(1, 2)); System.out.println("CalculatorClient - Using InternalAdditionProvider class"); System.out.println("Output = " + new InternalAdditionProvider().add(1, 1)); } } |
Compiler error:
1 |
The type com.itsallbinary.internal.InternalAdditionProvider is not accessible |
Conclusion:
So now authors of libraries have encapsulate code with more control & in precise manner without losing freedom of project structure inside library itself.
This is a basic example to explain modules. To get more in-depth details on module, refer https://www.oracle.com/corporate/features/understanding-java-9-modules.html