Java 19: Pattern Matching for switch Expressions

One of the great additions to the Java core language features was pattern matching introduced with Java 17. Pattern matching is a fancy term for a very useful new mechanic that reduces the need to cast objects and explicitly declare variables when using switch statements or the instanceof operator.

As a quick refresher let’s look at a simple example for pattern matching that is valid since Java 17.

As a starting point, we have a simple class hierarchy of two different birds:

sealed interface Bird permits Thunderbird, Hummingbird {
	String twitter();
}

final class Hummingbird implements Bird {
	public String twitter() {
		return "hummmmm";
	}
}

final class Thunderbird implements Bird {
	public String twitter() {
		return "";
	}
}

In line (1) I declare a sealed interface Bird with two possible implementations: the Hummingbird and the Thunderbird. Both of them implement the method twitter from the parent interface Bird.

Next, let’s write some code that uses pattern matching to differentiate between those two:

public static void twitter(Bird bird) {

  if (bird instanceof Hummingbird humm ) {
	System.out.println("Hummingburd twitters: "+humm.twitter() );
  } else if (bird instanceof Thunderbird thunder) {
	System.out.println("Thunderbird twitters: "+thunder.twitter());
  }
}

In line (3) and (5) you can see pattern matching in action. By checking the type of the bird variable by the instanceof operator, I declare a variable of the same type at the same time. Just to see the difference, here is the pre Java 17 version of this code:

public static void twitterOld(Bird bird) {
  if (bird instanceof Hummingbird) {
    Hummingbird humm = (Hummingbird)bird;
    System.out.println("Hummingburd twitters: "+humm.twitter());
  } else if (bird instanceof Thunderbird ) {
    Thunderbird thunder = (Thunderbird)bird;
    System.out.println("Thunderbird twitters: "+thunder.twitter());
  }
}

In line (3) and (6) we can see the variable definition and casting that we needed although it was already clear in line 2 and 5 what type of object we were dealing with.

You might think, that this would be better done in a switch statement and luckily, with Java 19 released, there is another preview feature that let us do exactly that. The enhancements are in detail explained in JEP 427, so check that JEP out if you want to know all the nasty details.

Let’s rewrite the above code to use the new switch statement:

public static void switchTwitter(Bird bird) {
  switch( bird ) {
    case Hummingbird humm -> 
      System.out.println("Hummingbird twitters:"+humm.twitter());
    case Thunderbird thunder -> 	
      System.out.println("Thunderbird twitters:"+thunder.twitter());
  }  
}

This looks a lot cleaner than the previous example using instanceof. You can simple test on the type of an object in the case statement and declare a local variable at the same time. The conditional code which should be executed follows the case in something that looks a lot like a lambda expression. This new mechanic is also sometimes referred to as a type switch.

But that’s not all you can do with the enhanced switch statement. Let’s expand our object model a little and introduce a method age() on the interface level, which returns the age of the bird:

sealed interface Bird permits Thunderbird, Hummingbird {
  String twitter();
  int age();	
}

final class Hummingbird implements Bird {

  private int age;
	
  public Hummingbird(int age) {
    this.age = age; 
  }
	
  public int age() { return age; }
	
  public String twitter() { return "hummmmm"; }
}

final class Thunderbird implements Bird {
	
  private int age;
	
  public Thunderbird(int age) {
    this.age = age; 
  }

  public int age() { return age; }

  public String twitter() { return ""; }
}

In this preview of the switch pattern matching feature you can check for the type of the object and also check for a boolean expression using the object at the same time. This is very handy if your logic depends not only on the type but different properties of your object.

Here is an example that checks for the age of the birds inside the switch statement :

public static void switchTwitterForYoungBirds(Bird bird) {
  switch( bird ) {
    case Hummingbird humm when humm.age() < 3 -> 
      System.out.println("Hummingbird twitters: "+ humm.twitter() );
    case Thunderbird thunder when thunder.age() < 5 -> 	
      System.out.println("Thunderbird twitters: "+thunder.twitter());
    default -> System.out.println("Bird is too old to twitter.");
  }
}

In line (3) and (5) we can see a so called guarded pattern in action. First the variable humm is declared if the provided Bird object is of type Hummingbird. After this, the keyword when is used to start a boolean expression. This expression can also consist of more nested boolean logic as long as it evaluates to a single boolean. If the expression is true, the code after the arrow is executed. Finally, I covered all cases with older birds by a default case, that again prints some text to the console. This reduces the required code a lot compared to nested if statements inside your case blocks.

One more thing to mention is, that a type switch statement must always cover all possible types of the type hierarchy. If you have no default clause, the compiler will throw a compile error indicating that not all cases are covered. That’s pretty neat if somebody extends a class hierarchy and forgets about updating all the type switches the hierarchy is used in.

To complete this overview, the new type switch also accepts one null value statement. Here is a small example for our bird type switch with a null case:

It is really great that Oracle spends a good chunk of work on introducing new Java language features and tries to remove lengthy boilerplate code, that Java is so famous of. With the improved pattern matching, Java code is much more modern, easy to read and to understand.

If you want to check out these examples yourself, you can find the code in this github repository.

The improved switch statement was released as a preview feature in Java 19. To compile and run your code with previews enabled, check out this page.


Beitrag veröffentlicht

in

von

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert