Java 11 Cookbook
上QQ阅读APP看书,第一时间看更新

How to do it...

  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 );
        }
  1. Implement the sumOfFirstNPrimes(Integer count) API:
        public static Integer sumOfFirstNPrimes(Integer count){
          return IntStream.iterate(1,i -> i+1)
                          .filter(j -> isPrime(j))
                          .limit(count).sum();
        }

  1. Let's write a function to check whether the number is even:
        public static Boolean isEven(Integer number){
          return number % 2 == 0;
        }
  1. 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))
                          .limit(count).sum();
        }

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

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) .filter(filter) .limit(count).sum(); }

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

If you are indeed wondering, then you need not worry, as we will cover these things in Chapter 4, Going Functional, and Chapter 5, Streams and Pipelines.

So far, we have seen a few APIs around mathematical computations. These APIs are part of our com.packt.math.MathUtil class. The complete code for this class can be found at Chapter03/2_simple-modular-math-util/math.util/com/packt/math, in the codebase downloaded for this book.

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. Place 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 module-info.java.
  3. Place the packages and the code files under the root directory.

What does module-info.java 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

As mentioned in Chapter 1, Installation and a Sneak Peek into Java 11, 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.

The code for this module can be found at  Chapter03/2_simple-modular-math-util/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:

  • Prime 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:

switch(choice){
  case 1:
    System.out.println("Enter the number");
    Integer number = reader.nextInt();
    if (MathUtil.isPrime(number)){
      System.out.println("The number " + number +" is prime");
    }else{
      System.out.println("The number " + number +" is not prime");
    }
  break;
  case 2:
    System.out.println("Enter the number");
    Integer number = reader.nextInt();
    if (MathUtil.isEven(number)){
      System.out.println("The number " + number +" is even");
    }
  break;
  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)));
  break;
  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)));
  break;
  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)));
  break;
}

The complete code for the Calculator class can be found at Chapter03/2_simple-modular-math-util/calculator/com/packt/calculator/Calculator.java.

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.

The code for this module can be found at Chapter03/2_simple-modular-math-util/calculator.

Let's compile the code:

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

The preceding command has to be executed from Chapter03/2_simple-modular-math-util.

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.

After running the preceding command, you will see the calculator in action:

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

We have provided scripts to test out the code on both Windows and Linux platforms. Please use run.bat for Windows and run.sh for Linux.