From Scala to Java. Encapsulate code with inner static classes, interfaces and enums.
Tags: java, scala, cleancode
Published: 2021-11-06 13:48:47

Using two languages causes you try to apply some concepts from one language to the other. Scala developers often say they started to commonly use `final` word with class field in Java. Similarly, one language often evolves inspired by other languages. For example, from Java 8 developers can use `deafult` interface method (similar to Scala `trait`), from Java 10 there is `var` keyword for declaring local variables, pattern matching implemented with `switch` statements gone through a rapid evolution since Java 7. 

I would like to share with you how Scala improves my Java code. In this post I will describe why I started commonly use inner static classes, enums and interfaces.

It is common practice to defining every Java class in separated file. You often meet every class, enum and interface placed in dedicated file. Let's start with example. Consider service to customer payments history verification (`CustomerVerificationService`). It can result with positive or negative loan decision. Verification consists of few steps: first system checks if customer is blacklisted, next if customer has internal debts (in relation to our company), at the end if external services reveals customer debts.

![Example: how loan decision is made](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqamz52pata5kvwjfdfb.jpg)

`CustomerVerificationService#verifyPaymentHistory` can return:
* `BlackListedCustomer(String reason)` - customer is blacklisted, credit decision is negative; result contains reason of blacklisting.
* `InternalDebt(String agreementNumber, BigInteger debtAmount)` - customer isn't blacklisted, but is our company debtor, credit decision is negative; result contains debt agreement number and debt value.
* `ExternalDebt(String debtSource, BigInteger debtAmount)` - customer isn't blacklisted, has no debt in our company but external services reveal debt, credit decision is negative; result contains debt value and its source (external service name).
* `PositiveDecision(BigInteger maxAmount)` - customer is not blacklisted and has no debts (internal or external), credit decision is positive; result contains maximum allowed loan value.

Above service interface can be implemented with Java 11 (LTS) as following:

```java
public class CustomerVerificationService {

  public VerificationResult verifyPaymentHistory(Customer customer) {
    //omitted code
    return new PositiveDecision(BigInteger.valueOf(6000L));
  }
}
```
```java
public class Customer {
  private final String pesel;
  private final String firstName;
  private final String lastName;
  private final String idNumber;

  public Customer(String pesel, String firstName, String lastName, String idNumber) {
    this.pesel = pesel;
    this.firstName = firstName;
    this.lastName = lastName;
    this.idNumber = idNumber;
  }

  public String getPesel() {
    return pesel;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getIdNumber() {
    return idNumber;
  }
}
```
```java
public interface VerificationResult {
}
```
```java
public class BlackListedCustomer implements VerificationResult {
  private final String reason;

  public BlackListedCustomer(String reason) {
    this.reason = reason;
  }

  public String getReason() {
    return reason;
  }
}
```
```java
public class InternalDebt implements VerificationResult {
  private final String agreementNumber;
  private final BigInteger debtAmount;

  public InternalDebt(String agreementNumber, BigInteger debtAmount) {
    this.agreementNumber = agreementNumber;
    this.debtAmount = debtAmount;
  }

  public String getAgreementNumber() {
    return agreementNumber;
  }

  public BigInteger getDebtAmount() {
    return debtAmount;
  }
}
```
```java
public class ExternalDebt implements VerificationResult {
  private final String debtSource;
  private final BigInteger debtAmount;

  public ExternalDebt(String debtSource, BigInteger debtAmount) {
    this.debtSource = debtSource;
    this.debtAmount = debtAmount;
  }

  public String getDebtSource() {
    return debtSource;
  }

  public BigInteger getDebtAmount() {
    return debtAmount;
  }
}
```
```java
public class PositiveDecision implements VerificationResult {
  private final BigInteger maxAmount;

  public PositiveDecision(BigInteger maxAmount) {
    this.maxAmount = maxAmount;
  }

  public BigInteger getMaxAmount() {
    return maxAmount;
  }
}
```

We have here as many as 7 files. Situation is quite different in Scala, single file often contains definition of several `classes`, `objects`, `traits`,`case classes`. For example:

```scala
class CustomerVerificationService {
  
  def verifyPaymentHistory(customer: Customer): Result = {
    //omitted code
    PositiveDecision(6000)
  }
}

object CustomerVerificationService {
  
    case class Customer(pesel: String, firstName: String, lastName: String, idNumber: String)
  
    sealed trait Result
    object Results {
      case class InternalDebt(agreementNumber: String, debtAmount: BigInt) extends Result
      case class ExternalDebt(debtSource: String, debtAmount: BigInt) extends Result
      case class BlackListedCustomer(reason: String) extends Result
      case class PositiveDecision(maxAmount: BigInt) extends Result
    }
}
```

Apart from the huge difference in code volume, pay attention how Scala example nicely encapsulates service interface. Service `CustomerVerificationService` has single public method `verifyPaymentHistory`. In the same file you will find definition of method input: `Customer` class. Every possible result of this  operation is instance of `Result` trait. Moreover, all `Result` descendant are grouped in `Results` object. Thanks to it, if you will start typing `CustomerVerificationService.Results.` IDE will prompt with all possible Results. Once again, pay attention how comfortably you can get all information about `CustomerVerificationService#verifyPaymentHistory`. You need to look only into single file. But good news! You can write 
in a similar manner in Java! All you need to do is use inner static classes and inner interfaces.

```java
public class CustomerVerificationService {

  public  Result verifyPaymentHistory(Customer customer) {
    //omitted code
    return new Results.PositiveDecision(BigInteger.valueOf(6000L));
  }

  public static class Customer {
    private final String pesel;
    private final String firstName;
    private final String lastName;
    private final String idNumber;

    public Customer(String pesel, String firstName, String lastName, String idNumber) {
      this.pesel = pesel;
      this.firstName = firstName;
      this.lastName = lastName;
      this.idNumber = idNumber;
    }

    public String getPesel() {
      return pesel;
    }

    public String getFirstName() {
      return firstName;
    }

    public String getLastName() {
      return lastName;
    }

    public String getIdNumber() {
      return idNumber;
    }
  }

  public interface Result { }
  public interface Results {
    class BlackListedCustomer implements Result {
      private final String reason;

      public BlackListedCustomer(String reason) {
        this.reason = reason;
      }

      public String getReason() {
        return reason;
      }
    }
    class InternalDebt implements Result {
      private final String agreementNumber;
      private final BigInteger debtAmount;

      public InternalDebt(String agreementNumber, BigInteger debtAmount) {
        this.agreementNumber = agreementNumber;
        this.debtAmount = debtAmount;
      }

      public String getAgreementNumber() {
        return agreementNumber;
      }

      public BigInteger getDebtAmount() {
        return debtAmount;
      }
    }
    class ExternalDebt implements Result {
      private final String debtSource;
      private final BigInteger debtAmount;

      public ExternalDebt(String debtSource, BigInteger debtAmount) {
        this.debtSource = debtSource;
        this.debtAmount = debtAmount;
      }

      public String getDebtSource() {
        return debtSource;
      }

      public BigInteger getDebtAmount() {
        return debtAmount;
      }
    }
    class PositiveDecision implements Result {
      private final BigInteger maxAmount;

      public PositiveDecision(BigInteger maxAmount) {
        this.maxAmount = maxAmount;
      }

      public BigInteger getMaxAmount() {
        return maxAmount;
      }
    }
  }
}
```

Comparing to Scala example there still is much more code lines. But the main advantage of this approach is better encapsulation. You don't create separated file for every interface and class. Especially since they don't make sense outside the context of `CustomerVerificationService` class. Pay also attention I changed name of `VerificationResult` interface to `Result`. Due to the fact `Result` is part of `CustomerVerificationService` class, its name may be less precise.  

But still, if code volume bother you, Java14 comes to the rescue. More specifically Java 14 introduces `record` keyword which is closer to Scala `class` even than Java `class`. With Java `record` you can reduce earlier implementation of `BlackListedCustomer` to one line:
```Java
record BlackListedCustomer(String reason) implements Result { }
```
The Java compiler auto generates getter methods, `toString()`, `hashcode()` and `equals()` methods, so you don't have  to write that boilerplate code yourself. Since a Java `record` is immutable, no setter methods are generated. Final version (in Java 14) may look like:

```java
public class CustomerVerificationService {

  public  Result verifyPaymentHistory(Customer customer) {
    //omitted code
    return new Results.PositiveDecision(BigInteger.valueOf(6000L));
  }

  public static record Customer(String pesel, String firstName, String lastName, String idNumber) { }
  public interface Result { }
  public interface Results {
    record BlackListedCustomer(String reason) implements Result { }
    record InternalDebt(String agreementNumber, BigInteger debtAmount) implements Result { }
    record ExternalDebt(String debtSource, BigInteger debtAmount) implements Result { }
    record PositiveDecision(BigInteger maxAmount) implements Result { }
  }
}
```

Look how above implementation is similar to the Scala example. 

In this post I wanted to show how nicely and easy you can encapsulate your code using inner static classes, enums and interfaces. Pay also attention how Java evolution made it simpler. Java 14 `record` keyword hiding boilerplate code and lets you focus on implementation.

---
Originally published at https://stepniewski.tech.

All Java examples from this post you will find at my [github](https://stepniewski.tech/L/34QHMmzh1).