Blog post

Increase readability with Java's Pattern Matching

Jonathan Vila

Jonathan Vila

Developer Advocate - Java

5 min read

  • Clean Code
  • Java

I bet you don’t like writing ugly but necessary boilerplate code or reading it. But, sometimes we need to create logic that has to deal with an object of unknown type and follow different paths depending on the type. This code is prone to be too verbose, is complex to understand, and may involve some hidden errors hard to spot due to intermediate assignments.

In this article, I will show different ways of checking the type of an object and keeping the code easy to understand while also reducing the chances of introducing bugs hard to spot.

The usage of instanceOf

In Java, we’ve been using instanceOf conditional statements, type casting, and temporary assignments for that purpose.

public String processElement(Object element) {
  String result;

  if (element instanceOf String) { 
    String elementStr = (String) element;
    result = elementStr;
  } else if (element instanceOf Person) {
    Person elementPerson = (Person) element;
    result = elementPerson.getName();
  }

  return result + " value";
}

This involves a lot of boilerplate code which is not very readable. But even more important, it allows coding errors to remain hidden. In this structure, nothing is ensuring we are assigning a value to the intermediate variable `result` and that could mean having an empty value at the end.

But Java has included new features since version 14 that will help us to improve in this area. Let’s discover them.

Pattern Matching 

In Java 16 an improvement was added in order to reduce code repetition and boilerplate: Pattern Matching for instanceOf cases. With this approach, the cast is included in the condition which is easier to read, reducing the boilerplate code.

public String processElement(Object element) {
  String result;

  if (element instanceOf String s) { 
   result = s;
  }

  if (element instanceOf Person elementPerson) {
    result = elementPerson.getName();
  }

  return result + " value";
}

With this change, we avoid the need for an extra type-cast, that is making the code harder to read, and even can involve more errors. 

Yes, I agree with you, this is not solving the problem entirely. We have improved but we are not there yet. Let’s see if Java provides more tricks ...

Pattern matching in switch cases

In order to improve the readability a bit and reduce complexity we can use a switch/case statement. With this approach, we get rid of the “else if” clauses, making it clear that cases are exclusive and have different branches. 

public String processElement(Object element) {
  String result;

  switch (element) {
   case (String s): 
     result = s;
     break;
   case (Person elementPerson):
    result = elementPerson.getName();
    break;
  }

  return result + " value";
}

But this code is still hard to read, I know. And it’s still weak in terms of errors that can happen by missing one break or by not assigning the value to the intermediate variable.

Switch expressions

In order to fix this situation we can use a very interesting feature included in Java 14: switch expressions. We will reduce the code even more, increase the readability and clarity, and avoid the bugs caused by missing intermediate assignments.

Also, we reduce the cognitive complexity of the resulting code by half and this positively impacts the readability and maintainability of the code. We need to keep in mind that too high complexity is one of the most common issues detected by Sonar tools in all the thousands of projects analyzed.

  return switch (obj) {
    case Person person-> String.format("Person %s", person.getName());
    case String s -> String.format("Str %s", s);
    default -> obj.toString();
  } + " value";


With this approach, we have a very clear idea of what the code is doing and also reduce the risk of errors.

If you want to calculate the cognitive complexity of your code, you can use the "Code complexity" plugin (in IntelliJ) that will give you a hint of your method’s complexity.

private void getStringsUsingInstanceOfIfs(Object user) { @ simple(25%)
    ...
}

private void getStringsUsingSwitchExpressionPattern(Object user) { @ simple(0%)
    ...
}


The Sonar Java analyzer will warn you if your code has too high complexity, and also will suggest using the switch pattern matching approach and the switch expression in order to improve readability.

Conclusions

We spend way more time reading code than writing it, so it’s super important to make our code conventional and intentional in order to help us understand its purpose.


The Java language adds new features in every release to help you write consistent, simple, and robust code providing standardized ways of solving common issues and reducing the time to understand the purpose of the code and the probability of errors.


Remember that SonarLint, SonarQube, and SonarCloud with their Java analyzer will help you deliver clean code with a long list of rules to consider when you code.


Get new blogs delivered directly to your inbox!

Stay up-to-date with the latest Sonar content. Subscribe now to receive the latest blog articles. 

By submitting this form, you agree to the storing and processing of your personal data as described in the Privacy Policy and Cookie Policy. You can withdraw your consent by unsubscribing at any time.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.