Develop Modular Apps in Java

In this lesson, we will learn how we can divide our java applications into simple and manageable modules. Modularity in programming refers to the extent to which a software or Web application may be divided into smaller modules or pieces. So the main focus of this tutorial will be to show you how you can create a modular application in Java.

Our example for this tutorial will be fairly simple, we will make a calculator that will simply check whether a number is prime, even or odd and then it will calculate the sum.

Let’s Start

We will divide our application into two modules:

math.util module, which contains the APIs for performing the mathematical calculations.

calculator module, which launches an advanced calculator.

Step 1

Let’s implement the APIs in the com.packt.math.MathUtil class, starting with the isPrime(Integer number) API:

 public static Boolean isPrime(Integer number){
      if ( number == 1 ) { return false; }
      return IntStream.range(2,num).noneMatch(i -> num % i == 0 );

Step 2

Implement the sumOfFirstNPrimes(Integer count) API:

  public static Integer sumOfFirstNPrimes(Integer count) {
      return IntStream.iterate(1,i -> i+1)
             .filter(j -> isPrime(j))

Step 3

Let’s write a function to check whether the number is even:

 public static Boolean isEven(Integer number) {
    return number % 2 == 0;

Step 4

The negation of isEven tells us whether the number is odd. We can have functions to find the sum of the first N even numbers and the first N odd numbers, as shown here:

 public static Integer sumOfFirstNEvens(Integer count) {
    return IntStream.iterate(1,i -> i+1)
            .filter(j -> isEven(j))

 public static Integer sumOfFirstNOdds(Integer count) {
    return IntStream.iterate(1,i -> i+1)
              .filter(j -> !isEven(j))

We can see in the preceding APIs that the following operations are repeated:

  • An infinite sequence of numbers starting from 1
  • Filtering the numbers based on some condition
  • Limiting the stream of numbers to a given count
  • Finding the sum of numbers thus obtained

Based on our observation, we can refactor the preceding APIs and extract these operations into a method, as follows:

Integer computeFirstNSum(Integer count, IntPredicate filter) {
   return IntStream.iterate(1,i -> i+1)

Here, count is the limit of numbers we need to find the sum of, and filter is the condition for picking the numbers for summing.

Let’s rewrite the APIs based on the refactoring we just did:

public static Integer sumOfFirstNPrimes(Integer count){
  return computeFirstNSum(count, (i -> isPrime(i)));

public static Integer sumOfFirstNEvens(Integer count){
  return computeFirstNSum(count, (i -> isEven(i)));

public static Integer sumOfFirstNOdds(Integer count){
  return computeFirstNSum(count, (i -> !isEven(i)));

You must be wondering about the following:

  • The IntStream class and the related chaining of the methods
  • The use of -> in the code base
  • The use of the IntPredicate class

Let’s make this small utility class part of a module named math.util. The following are some conventions we use to create a module:

  1. lace all the code related to the module under a directory named math.util and treat this as our module root directory.
  2. In the root folder, insert a file named
  3. Place the packages and the code files under the root directory.

What does contain? The following:

  • The name of the module
  • The packages it exports, that is, the one it makes available for other modules to use
  • The modules it depends on
  • The services it uses
  • The service for which it provides implementation

The JDK comes bundled with a lot of modules, that is, the existing Java SDK has been modularized! One of those modules is a module named java.base. All of the user-defined modules implicitly depend on (or require) the java.base module (think of every class implicitly extending the Object class).

Our math.util module doesn’t depend on any other module (except, of course, the java.base module). However, it makes its API available for other modules (if not, then this module’s existence is questionable). Let’s go ahead and put this statement into code:

module math.util{
  exports com.packt.math;

We are telling the Java compiler and runtime that our math.util module is exporting the code in the com.packt.math package to any module that depends on math.util.

Now, let’s create another module calculator that uses the math.util module. This module has a Calculator class whose work is to accept the user’s choice for which mathematical operation to execute and then the input required to execute the operation. The user can choose from five available mathematical operations:

  • rime number check
  • Even number check
  • Sum of N primes
  • Sum of N evens
  • Sum of N odds

Let’s see this in code:

private static Integer acceptChoice(Scanner reader){
  System.out.println("**********Advanced Calculator**********");
  System.out.println("1. Prime Number check");
  System.out.println("2. Even Number check");
  System.out.println("3. Sum of N Primes");
  System.out.println("4. Sum of N Evens");
  System.out.println("5. Sum of N Odds");
  System.out.println("6. Exit");
  System.out.println("Enter the number to choose operation");
  return reader.nextInt();

Then, for each of the choices, we accept the required input and invoke the corresponding MathUtil API, as follows:

  case 1:
    System.out.println("Enter the number");
    Integer number = reader.nextInt();
    if (MathUtil.isPrime(number)){
      System.out.println("The number " + number +" is prime");
      System.out.println("The number " + number +" is not prime");
  case 2:
    System.out.println("Enter the number");
    Integer number = reader.nextInt();
    if (MathUtil.isEven(number)){
      System.out.println("The number " + number +" is even");
  case 3:
    System.out.println("How many primes?");
    Integer count = reader.nextInt();
    System.out.println(String.format("Sum of %d primes is %d", 
          count, MathUtil.sumOfFirstNPrimes(count)));
  case 4:
    System.out.println("How many evens?");
    Integer count = reader.nextInt();
    System.out.println(String.format("Sum of %d evens is %d", 
          count, MathUtil.sumOfFirstNEvens(count)));
  case 5: 
    System.out.println("How many odds?");
    Integer count = reader.nextInt();
    System.out.println(String.format("Sum of %d odds is %d", 
          count, MathUtil.sumOfFirstNOdds(count)));

Let’s create the module definition for our calculator module in the same way we created it for the math.util module:

module calculator{
  requires math.util;

In the preceding module definition, we mentioned that the calculator module depends on the math.util module by using the required keyword.

Let’s compile the code:

javac -d mods --module-source-path . $(find . -name "*.java")

Also, you should have the compiled code from across both the modules, math.util and calculator, in the mods directory. Just a single command and everything including the dependency between the modules is taken care of by the compiler. We didn’t require build tools such as ant to manage the compilation of modules.

The –module-source-path command is the new command-line option for javac, specifying the location of our module source code.

Let’s execute the preceding code: 

java --module-path mods -m calculator/com.packt.calculator.Calculator

The –module-path command, similar to –classpath, is the new command-line option  java, specifying the location of the compiled modules.

Congratulations! With this, we have a simple modular application up and running. 

Muhammad Mubeen

Muhammad Mubeen

Mubeen is a full-stack web & mobile app developer who is very proficient in MEAN.js, Vue, Python, Ionic 4, Flutter, Firebase, ROR, and PHP. He has created multiple mobile and web applications. He is very passionate about sharing his knowledge.

Leave a Reply

Your email address will not be published. Required fields are marked *