Decorator Pattern using Java 8

In this post we are going to look at a couple of examples which use the GoF Decorator pattern. We will then refactor this code to use some of the features introduced in Java 8 which give Java a taste of functional programming. After this refactoring, we will see if the refactored code showcasing the decorator pattern becomes concise and easy to understand or makes it more complicated.

The basic idea behind the decorator pattern is that it allows us to add behavior to an object without affecting or modifying the interface of the class. I would suggest you to read and get a basic idea about the Decorator pattern in case you are completely unaware of it.

I would be considering 2 existing posts showcasing the decorator pattern and then we would be refactoring the code using Java 8.

Typical Decorator – Example one

The first example that we are going to look at is a code sample that I saw from a site, codeofdoom.com, which unfortunately does not exist anymore(domain seems to have expired). That post referred to an example which was simple to read and showed how a typical decorator pattern is implemented. I thought I had understood the pattern but I was mistaken.

The requirement in that example

The requirement that I understood from the code – We need to format a particular text supplied as input. The formatting can be one of several options- we return the text as it is, format the text to upper case, replace a word in the text with another word , concatenation of text with another string or a permutation and combination of the options. I have stated the requirements upfront but usually as developers we are never aware of the requirements upfront and requirements always keep changing. It would not make sense to create separate classes for each permutation – combination. In such situations, the decorator pattern can be extremely useful.

The example mentioned has an interface Text and a basic implementation of the same which returns the text. There are 3 classes AllCaps, StringConcat and ReplaceThisWithThat which are decorators taking the input text and adding specific behavior to it. The advantage of using the decorator pattern here is that we can use and combine 1 or more decorators to achieve formatting of the input text dynamically rather than sub-classing and creating different combinations of the sub-classes.However the implementation of a typical decorator is not so straightforward to understand. I had to debug a little bit to get a good understanding of how the code really decorates the object.

So let’s define an interface Text with a method format as shown below –

public interface Text {
    public String format(String s);
}

The BaseText class simply returns the text as is –

public class BaseText implements Text{

    public String format(String s){
        return s;
    }
}

The TextDecorator class serves as base decorator class which other classes extend. This decorator class is like a core class which helps in combining different functionalities.

public abstract class TextDecorator implements Text {

    private Text text;

    public TextDecorator(Text text) {
        this.text = text;
    }

    public abstract String format(String s);
}

The AllCaps class takes the input and formats it to uppercase –

public class AllCaps extends TextDecorator{

    public AllCaps(Text text){
        super(text);
    }

    public String format(String s){
        return text.format(s).toUpperCase();
    }
}

The StringConcat class calls format and then concatenates the input string –

public class StringConcat extends TextDecorator{

    public StringConcat(Text text){
        super(text);
    }

    public String format(String s){
        return text.format(s).concat(s);
    }
}

And finally, the class which replaces text “this” with “that” –

public class ReplaceThisWithThat extends TextDecorator{

    public ReplaceThisWithThat(Text text){
        super(text);
    }

    public String format(String s){
        return text.format(s).replaceAll("this","that");
    }
}

Test class to run the decorators –

public class TextDecoratorRunner{

    public static void main(String args[]){

        Text baseText = new BaseText();
                
        Text t = new ReplaceThisWithThat(new StringConcat(new AllCaps(baseText)) );

        System.out.println(t.format("this is some random text"));
    }
}

Notice how the call is done. It is actually inside-out but the code is read from the outside-in.

A pixel is worth 1024 bits
Flow of the Decorator pattern

The left hand side shows the calls to the format method in each decorator class and the right hand side shows how the decorators hold references or how they are chained.

The final output from this is – THIS IS SOME RANDOM TEXTthat is some random text.

Refactoring the decorator pattern using Java 8

Well, now to the main task of re-implementing the same using functional style. The BaseText class shown above has a function, format, which simply takes a String and returns a String. This can be represented using functional interface, Function<String,String> – introduced in Java 8.

import java.util.function.Function;

public class BaseText implements Function<String,String>{

    public BaseText(){
    }

    @Override
    public String apply(String t) {
        return t;
    }
}

Now, each decorator class implements a simple functionality of formatting a string in different ways and can be combined as shown below in a single class using static methods.

public class TextDecorators
{

    public static String allCaps(String s){
        return s.toUpperCase();
    }

    public static String concat(String input) {
        return input.concat("this is some random text");
    }

    public static String thisWithWhat(String input) {
        return input.replaceAll("this", "that");
    }

}

This simply leads us to the following –

public class TextDecoratorRunner {

    public static void main(String[] args) {
        String finalString = new BaseText()
                .andThen(TextDecorators::allCaps)
                .andThen(TextDecorators::concat)
                .andThen(TextDecorators::thisWithWhat)
                .apply("this is some random text");

        System.out.println(finalString);
    }
}

The code above does function chaining and uses method reference. It takes the text and passes it through a chain of functions which act as decorators. Each function applies a decoration and passes the decorated string as output to the next function which treats it as an input.  In this scenario, there is no need to have separate classes which act as decorators or the abstract base class. This implementation offers the same flexibility as the typical solution but I think it is also much much easier to understand as compared to the typical way of implementing the decorator pattern. There is also no inside-out or outside-in business when we call the function.

Typical Decorator – Example two

The second example that we are going to consider is shown here groovy decorator.  I would suggest you to read it to get a basic understanding of the use case. Also note the confusion between whether the complete text is logged in upper case or timestamp is logged in lower case.

Decorator pattern refactored

Let us try and use the same concept of function chaining to refactor this.

The code for the Logger interface would look like this –

public interface Logger {
	public void log(String message);
}

The SimpleLogger class –

public class SimpleLogger implements Logger {

	@Override
	public void log(String message) {
		System.out.println(message);

	}
}

The individual decorated logger can be represented as below –

import java.util.Calendar;

public class Loggers {
	
	public static String withTimeStamp(String message){
		
		Calendar now = Calendar.getInstance(); 
		return now.getTime() + ": "+  message;
	}
	
	public static String uppperCase(String message){
		
		return message.toUpperCase();
	}
}

And finally using function chaining –

import java.util.function.Function;
import java.util.stream.Stream;

public class LoggerTest {
	public static void main(String[] args) {

Logger logger = new SimpleLogger();

        Function<String,String> decorators = Stream.<Function<String,String>>of(Loggers::withTimeStamp,Loggers::uppperCase)
                .reduce(Function.identity(),Function::andThen);

        logger.log(decorators.apply("G'day Mate"));

	}
}

The code above looks a little daunting at first glance but it is actually simple, let us break it down –

Lines 9- 12 are the key to understanding this. I am using Stream.of and passing method references which are individual decorators.

  1. To combine them or to chain them, we use the Stream.of. The Function<String,String> in Stream.<Function<String,String>>of  is more of a compilation thing , to represent the Stream that the output is a Function<String,String>.  Using the Stream.of, we are forming a chain of functions.
  2. Now to the reduce part, the reduce part chains all of them together to form a single function. What do we want to do with each function as they come out from the stream ? We want to combine them, this is done using the andThen, that is exactly done in the 2nd parameter to the reduce function.  The first parameter to the reduce method is an initial value, this is the identity function – given a function, just return the function. Every reduction needs a starting point. In this case, the starting point is the function itself.
  3. The chaining yields a Function<String,String> decorators.
  4. We just call the apply method to this using the parameter G’day Mate which passes it through the chain of functions( decorators) and finally sends that to the logger.log method.

This version of the decorator pattern is easier to understand, we simply took a message, added the timestamp to it , converted it to upper case and logged it.The Stream.of and the methods references being passed to it might seem difficult at the first read, but trust me it is more of a new way of solving the problem and an easier one in my opinion.

Conclusion

We took 2 examples of the decorator pattern but both examples have clarified that there is certainly an easier and better way to implement the decorator pattern. In both cases the decorator pattern using the functional style is definitely more concise and easier to understand. Code that is easier to understand is always easy to maintain. Can we safely conclude that we probably don’t need the typical decorator pattern anymore?

Well, in the examples that we considered, we had just one method or one functionality to decorate, the format method in the first example and log method in the second example. In these scenarios, the functional style definitely seems better and there is no need to go about creating different classes, the abstract class and then use the cryptic way of calling them. But what if we had more than a single method to decorate ? This is something that needs to be looked at and I will try and answer that in another post.

Integration testing using Testcontainers in a Spring Boot 2, JPA application

In this post we will be looking at how to perform integration testing of the repository layer using Testcontainer. We will be setting up Testcontainer in a Spring Boot 2 application, use JPA for persistence and PostgreSQL as database.Besides this we will also be using JUnit5 framework.

Why Integration testing of the database ?

In any software application, it is a known fact that data is of utmost importance. In a web application, this data will usually be handled at the database layer. In an application which uses JPA for persistence, the repository and the JPA entities are major components of the database layer.

To ensure that the repository layer and the entities are functioning appropriately, we should write unit tests. Unit tests for the same are usually written against an in memory database. This is definitely required. The only catch here – Should we wait until production to ensure that the database layer is behaving as expected against our actual type of our database as against the in memory database ?

What would be even better is if we can do integration testing against a similar database that we use in production environment – MySQL,Oracle,PostgreSQL etc. But wouldn’t setting this up be difficult and laborious for just integration testing ?

Enter Testcontainers

Using Testcontainers, we can actually start a containerized instance of the database to test our database layer without going through the pain of actually setting it up on every developer’s machine. Testcontainer is a Java library that provides disposable and lightweight instances of databases. It actually does a lot more that just that. You can read about it here.

How does it help us ?

Using JUnit tests against the actual type of our production database will give us feedback before we actually go into production.This means we can worry less about our database layer and breathe a little easy. There will always be other issues in production,NullPointerExceptions in our code that we can worry about! Since Testcontainers spawns a containerized environment for us to run our tests, we need to have Docker installed.

Getting started with the code

We will be using Spring Boot 2.3.0 along with Spring Data JPA, JUnit 5 to run the tests,Flyway to create tables and TestContainer version 1.14.1.

Snippet of the pom.xml
<!-- The PostgreSQL db driver -->        
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>test</scope>
</dependency> 
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>testcontainers</artifactId>
	<version>${testcontainer.version}</version>
	<scope>test</scope>	
</dependency>
<!-- test container support for postgre module -->
<dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>${testcontainer.version}</version>
        <scope>test</scope>
</dependency>
<!-- JUnit 5 integration with testcontainer -->
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>junit-jupiter</artifactId>
	<version>${testcontainer.version}</version>
	<scope>test</scope>
</dependency>
The application.properties file
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL94Dialect
spring.jpa.show-sql=true

The spring.jpa.hibernate.ddl-auto has been set to none because we are using flyway to create our database and tables. The initialization script can be found under the /db/migration folder.

Starting the containerized PostgreSQL database and tying it with Spring
public abstract class AbstractContainerBaseTest {

	protected static final PostgreSQLContainer postgres;

	static {
		postgres = new PostgreSQLContainer("postgres:9.6.15")
										 .withDatabaseName("socialmediasite")
										 .withUsername("postgres")
										 .withPassword("password");
		//Mapped port can only be obtained after container is started.
		postgres.start();
	}
	
	static class PropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
		public void initialize(ConfigurableApplicationContext configurableApplicationContext) {			
			TestPropertyValues
					.of("spring.datasource.driver-class-name=" + postgres.getDriverClassName(),
							"spring.datasource.url=" + postgres.getJdbcUrl(),
							"spring.datasource.username=" + postgres.getUsername(),
							"spring.datasource.password=" + postgres.getPassword())
					.applyTo(configurableApplicationContext.getEnvironment());
		}
	}
}

The main purpose of this abstract class is to start the container using Testcontainer library. All other test classes will extend this class .The static block creates the container and this instance will be shared by all test classes. There is a manual call to start method. The library manages to shut it down automatically.

The PropertiesInitializer class is needed because we need to access this containerized database through the Spring Data JPA.

Initialization of Testcontainer,PostgreSQL, Spring Boot,Spring Data JPA
Verifying the output
PostgreSQL container created and closed by Testcontainer

I am using the docker ps –format command to list the containers started by Testcontainer library. As shown in the image above, one can see the container having the name competent_raman which is the PostgreSQL container.For all the test cases this is the single instance of the container. There is also this additional image quay.io/testcontainers/ryuk:0.2.3 – this is created by the Testcontainer library which ensures all other containers are shut down and cleaned when the JVM shuts down.

Database and tables in the containerized PostgreSQL database.

As you can see in the image above, I am using the docker exec -it command to connect to the same containerized instance, (competent_raman) of the PostgreSQL database. Then using the command psql -h 192.168.99.100 -p 32813, we are connected to the dockerized database. The -p indicates the port number and -h the host. The \c socialmediasite command connects us to the database and finally the \d command is used to list all the tables.

Once all the tests are executed, the Testcontainer library removes the instances of the containers that it created.

The JPA entities SocialMediaSite and User used in the application have a One-Many relationship.

Conclusion

Writing unit tests against an in-memory database like H2 is certainly helpful but writing integration tests against actual type of production database takes us to the next level – more confidence, fewer failures in production. The combination of Testcontainers, Spring Boot 2, JUnit5 makes it easy for us to get started on this journey. You can find the entire source code here.

Understanding @SpringBootApplication

The objective of this post is to get an understanding of what @SpringBootApplication annotation does in a Spring Boot application. Sometimes we just tend to add an annotation, everything works magically and we are happy.

But when we want to make a few changes, things start breaking, components are not found and then we blame the magic that we enjoyed earlier.So it is always a good idea to get a better understanding of what some of these annotations do.

In a Spring Boot application, we need a class with a main method annotated with @SpringBootApplication annotation as shown below:

package com.boot.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaApplication.class, args);
	}

}

This is the starting point for a Spring Boot application. The @SpringBootApplication annotation is really a short hand annotation or a combination of the following annotations:

1. @SpringBootConfiguration

This signifies that the application is not just a normal Spring application but a Spring Boot application. Actually this is just a combination of @Configuration+ Spring Boot, so more of a specialization. @Configuration applied on a file signifies that the class contains spring bean configurations using @Bean.

2. @EnableAutoConfiguration

This is the super intelligent annotation which kicks in the auto configuration for Spring Boot. Auto configuration is done by inspecting the classpath and configuring necessary beans that may be required. If you were to provide your own configuration, then Spring Boot will not re-configure the bean again.

How is this auto configuration done ?

Auto Configuration is done via a SpringFactoriesLoader class which will pick up and read the META-INF/spring.factories file.  It is like a hook into instantiating certain beans found on the classpath. A sample of the same is shown below. EnableAutoConfiguration class is the ‘key’ below and it can have many ‘values’ as classes. These are read and conditionally configured depending on what is already configured by the developer and what is found on the classpath. For example, only if RabbitMQ and Spring AMQP client are found on the classpath usually added via maven or gradle, then Spring Boot tries to configure RabbitMQ.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
....

3. @ComponentScan

Any Spring application will usually consist of beans which will be managed by Spring framework. To tell Spring framework to manage a bean, we use @ComponentScan. This annotation will scan all your beans and register it with the Spring Application Context. Later, you could inject the beans using @Autowired. Examples of a Component are classes annotated with @Controller, @Repository, @Service etc.

One has to be careful as @ComponentScan will scan the packages below the package from where this is defined.

A better way to organize your packages would be to have the  class containing the @SpringBootApplication in the root package of your application so that all sub packages get scanned and beans are registered with application context. If this is not possible for some reason, you can of course add @ComponentScan annotation and specify the base package(s) to scan for spring beans.

Conclusion

Annotations in the Spring or Spring Boot framework makes the life of a developer easy and increases productivity.However it is important to understand the ‘what and how’ of these annotations to be a more effective and efficient developer.

Java AutoCloseable : How does it work?

Most of us know that AutoCloseable is an interface that a class needs to implement to ensure that resources are automatically released. Let us consider a very simple example:

public class MyResource implements AutoCloseable{
    
	public void openConnection(){
		System.out.println("Opening expensive resource...");
	}
	
	public static void main(String args[]) throws Exception{
		
		MyResource myResource = new MyResource();
		myResource.openConnection();
	}

	@Override
	public void close() throws Exception {
		System.out.println("Releasing expensive resource...");		
	}
}

The output from this is simply :

Opening expensive resource…

The close method was never called.

AutoCloseable – Automatic Resource Management ?

We kind of assumed that implementing the AutoCloseable interface will magically do everything for us. Isn’t this feature also called Automatic Resource Management ? I think the automatic word can be a little misleading here.May be that is why this feature is also called try-with-resources. Well, as a programmer, I do forget things.

What we have clearly forgotten is to wrap the resource in a try block. If you read the java docs (which can be a bit boring at times), it is clearly mentioned that the resource needs to be wrapped in a try block. But I am sure we don’t really read the java docs that carefully. Most IDE’s now days show us a warning that the resource is never closed. Having said that, let us take a look at the byte code

public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: new #5 // class blog/MyResource
3: dup
4: invokespecial #6 // Method “<init>”:()V
7: astore_1
8: aload_1
9: invokevirtual #7 // Method openConnection:()V
12: return

On line 9 , there is a  invokeVirtual call to openConnection method and really nothing about the call to close method. There is a return after a call to the openConnection method.

Try block to the rescue

Let us go ahead and wrap the resource in a try block.

public class MyResource implements AutoCloseable{
    
	public void openConnection(){
		System.out.println("Opening expensive resource...");
	}
	
	public static void main(String args[]) throws Exception{
	
		try(MyResource myResource = new MyResource()){
			myResource.openConnection();	
		}
	}

	@Override
	public void close() throws Exception {
		System.out.println("Releasing expensive resource...");		
	}
}

This time the output is as expected:

Opening expensive resource…
Releasing expensive resource…

So putting a try around the resource did invoke the close method. But to understand this, let us take a look at the byte code:

public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: new #5 // class blog/MyResource
3: dup
4: invokespecial #6 // Method “<init>”:()V
7: astore_1
8: aconst_null
9: astore_2
10: aload_1
11: invokevirtual #7 // Method openConnection:()V
14: aload_1
15: ifnull 85
18: aload_2
19: ifnull 38
22: aload_1
23: invokevirtual #8 // Method close:()V
26: goto 85
29: astore_3
30: aload_2
31: aload_3

See line number 23 above. The compiler inserts the call to the close method after the openConnection method. This is done using invokeVirtual instruction.

Conclusion

If you implement the AutoCloseable interface and have an expensive resource which should be closed, remember to wrap the resource in a try block.

Testing JPA entities using Spring Boot 2, JUnit 5 and Java 14

In this article we will be looking at how to get started with testing JPA entities and the repository layer using Spring Boot 2.2 , JUnit 5 and Java 14. I have written a similar post here which uses Spring 1.5, JUnit 4 and Java 8. As the changes are significant I decided to keep them separate instead of updating that post.

I will be focusing mostly about the changes I had to make to the code to upgrade the libraries mentioned above. You can find the complete source code on github here.

Let us consider the same example of One-Many relation between SocialMediaSite and a User.

The @DataJpaTest

This remains the key ingredient behind running the JPA related tests in Spring Boot. @DataJpaTest disables full auto configuration and applies configuration related to JPA tests only. This concept hasn’t changed between Spring Boot 1.5 and 2.x. Now, let’s take a look at the areas where the code needed modifications.

Changes to the pom.xml

Upgrading the spring boot version to 2.2.6

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
</parent>

Upgrading java version to 14

<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>14</java.version>
	</properties>

Upgrading the dependency section to exclude JUnit 4

The spring-boot-starter-test dependency includes the vintage JUnit 4 and JUnit 5 dependencies.Since we will be using JUnit 5, we will exclude the vintage one.

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

Junit 5 consists of 3 different sub projects – The JUnit engine , JUnit vintage ( Junit 3 and 4) and JUnit Jupiter( JUnit5). All JUnit 5 annotations reside in org.junit.jupiter.api package.

Support for preview features in Java 14

<plugin>
	<artifactId>maven-compiler-plugin</artifactId>
	<configuration>
		<release>14</release>
		<compilerArgs>
		    <arg>--enable-preview</arg>
		 </compilerArgs>
					 
                <forceJavacCompilerUse>true</forceJavacCompilerUse>
		  <parameters>true</parameters>
	</configuration>
</plugin>

<plugin>
	<artifactId>maven-surefire-plugin</artifactId>
	<configuration>
		<argLine>--enable-preview</argLine>
	</configuration>
</plugin>
Use of the Record type

Record type has been added as a preview feature in Java 14. The EmailAddress class has now been modified as a Record type. This was a value object before.

public record EmailAddress(String emailAddress) {

	public static final Pattern VALID_EMAIL_ADDRESS_REGEX = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$",
			Pattern.CASE_INSENSITIVE);

	public EmailAddress {
		Assert.isTrue(isValid(emailAddress), "Email Address is Invalid !");
	}

	private static boolean isValid(String emailAddress) {
		return emailAddress != null ? VALID_EMAIL_ADDRESS_REGEX.matcher(emailAddress).matches() : false;
	}

}
Changes to the Test classes
  1. There is no need to add the @RunWith(SpringRunner.class) annotation anymore.
  2. The @Before annotation has been now replaced with @BeforeEach to perform any kind of setup before each test. Similarly the @After has been replaced with @AfterEach.
  3. The @Rule and @ExpectedException had been removed from JUnit5. The test classes have been refactored to reflect the same. The assertThrows and assertEquals method from the JUnit5 library have been used instead.
@Test
public void testShouldReturnExceptionForInvalidEmailAddress() {
		
	var exception = assertThrows(IllegalArgumentException.class, () -> new EmailAddress("test@.com"));
	assertEquals(exception.getMessage(), "Email Address is Invalid !");
}

The assertThrows method takes a functional interface, Executable as second parameter. We pass the block of code that needs to be executed to Executable.

Conclusion

The @DataJpaTest helps us to test the JPA entities and the repository layer. This is also called as test slicing as we are testing only the JPA components.The @DataJpaTest annotation contains @ExtendWith(SpringExtension.class) which integrates the Spring Testing framework with JUnit 5 model.

We have not explored many features of JUnit5 in this example but the combination of Spring Boot’s testing features and JUnit5 does make a powerful combination.

Switch on enums in Java 14

In this article we will be taking a look at switch statements and expressions on enums. If you are new to the switch expressions which was introduced in Java 14, take a look at my previous blog here.

Let us consider an Ecommerce application which maintains the status of an order. Let’s keep this simple and consider that the order can be in one of the following states – Placed,Confirmed,Cancelled,Delivered. This can be represented by using an enum as shown below:

public enum OrderStatus {
    PLACED,
    CONFIRMED,
    CANCELLED,
    DELIVERED
}

An order class contains this OrderStatus as shown below:

public class Order {
    private OrderStatus orderStatus;

    public void setOrderStatus(OrderStatus orderStatus) {
        this.orderStatus = orderStatus;
    }

    public OrderStatus getOrderStatus() {
        return orderStatus;
    }
}
Switch statement and enum

Consider a processOrder method which makes use of a switch statement as shown below:

private void processOrder(OrderStatus orderStatus) {
        String status = "";
        switch (orderStatus) {
            case PLACED, CONFIRMED, DELIVERED:
                status = "Success";
                break;
            case CANCELLED:
                status = "Fail";
                break;
        }
        //Further processing with status.
}

This code will compile without any problem. There is however one problem with this code. If we were to add a new state, (let us call it RETURNED) to the OrderStatus enum but forget to handle this in all switch cases,the code will silently fail as this case is not handled. The usual practice is to add a default case above so that the code does not silently fail without any indication.With a default case, the code can be modified to throw an error indicating to us that there is a change in the enum which we need to take care of.

   private void processOrder(OrderStatus orderStatus) {
        String status = "";
        switch (orderStatus) {
            case PLACED, CONFIRMED, DELIVERED:
                status = "Success";
                break;
            case CANCELLED:
                status = "Fail";
                break;
            default:
                throw new IllegalArgumentException("Invalid order status : " + orderStatus);
        }
        //Further processing with status.
    }
Switch expressions and enum

If we refactor the processOrder method using switch expressions,the code would look like this:

private void processOrder(OrderStatus orderStatus) {
        String status = switch (orderStatus) {
            case PLACED, CONFIRMED, DELIVERED -> "Success";
            case CANCELLED -> "Fail";
        };
        //Further processing with status.
}

If we were to introduce a new status RETURNED in the OrderStatus enum, the compiler immediately flags an error that the switch expression does not cover all the possible input values. We don’t need to write a default case that throws an Exception to handle this scenario.

private void processOrder(OrderStatus orderStatus) {
        String status = switch (orderStatus) {
            case PLACED, CONFIRMED, DELIVERED -> "Success";
            case CANCELLED -> "Fail";
        };
        //Further processing with status.
}

So in case of switch expressions on enums, the case blocks must cover all possible values.

Switch expression and default block

In case of switch expressions on an enum, the compiler inserts a implicit default block if the code does not already have one.This is extremely useful since it would indicate a change in the enum defintion. The default block is inserted as shown below: ( snippet from the .class file)

private void processOrder(OrderStatus orderStatus) {
        String var1;
        switch(orderStatus) {
        case PLACED:
        case CONFIRMED:
        case DELIVERED:
            var1 = "Success";
            break;
        case CANCELLED:
        case RETURNED:
            var1 = "Fail";
            break;
        default:
            throw new IncompatibleClassChangeError();
        }
    }

The default case is inserted if we don’t write one.

Conclusion

With the introduction of switch expressions in Java 14, code using enums in switch expressions is more robust. The compiler checks if all possible values have been handled and then inserts an implicit default case in case we don’t write one .

If the code containing the switch is not compiled and the enum is changed, the IncompatibleClassChangeError will signal an error. This feature will definitely avoid the silent failures that we have seen in switch statement using enums.

Switch expressions in Java 14

In this article we will be taking a look at switch expressions that have been incorporated in JDK 14 which was released on 17th March 2020.Switch expressions were made available as a preview language feature in JDK 12 and 13. Based on the feedback, changes were made and now this feature has been finalized in JDK 14.Let us start with a simple example of a switch block:

public int getNumberOfRooms(int budget) {
        int numberOfRooms = 0;
        switch (budget) {
            case 1000000:
                numberOfRooms = 1;
                break;
            case 2000000:
            case 3000000:
                numberOfRooms = 2;
                break;
            case 4000000:
            case 5000000:
                numberOfRooms = 3;
                break;
            default:
                System.out.println("Hello");
        }
        return numberOfRooms;
}
Vintage switch

The method above takes in a budget as parameter and depending on the budget it returns the number of rooms that one can buy with the budget. For simplicity, let us assume that we have a flat budget. 1 million can get us a single room. 2 and 3 million can get us 2 rooms and finally 4 and 5 million can get us 3 rooms.

Some observations about the usage of switch statement in the example above:

  1. Most usage of switch statements do some processing and return a value.
  2. Most case blocks have a break statement to avoid fall through otherwise it will execute the next case block till it encounters a break – it is quite easy to forget a break and well, adding a break serves the purpose but can make the code difficult to read at times. As experienced Java programmers, we are now used to it.
  3. The default case is optional and it does not enforce the programmer to throw an exception or return a value. I could add a System.out.println there and the code will compile and run.
  4. Multiple cases which require common logic to be processed have separate case labels on different lines with no break in between them which takes an effort to read and understand – Again we are used to it but imagine folks being introduced to the language for the first time.
The new modern switch
public int getNumberOfRooms(int budget) {

        int numberOfRooms = switch (budget) {
            case 1000000 -> 1;
            case 2000000, 3000000 -> 2;
            case 4000000, 5000000 -> 3;
            default -> throw new IllegalStateException("No rooms available for this budget");
        };
        return numberOfRooms;
}

  1. The new switch assigns/returns a value to a variable on the left side, hence called switch expression. Notice the semicolon on line 8 at the end of the switch block.
  2. Multiple cases are combined on the same line with a comma.
  3. The right hand side is separated with arrow (->) instead of a colon.
  4. No break in between cases, there is no fall through by default.
  5. The default case has to either return a value or throw an exception.
  6. Last but not the least and in fact an important difference is that this code is definitely more pleasing to the the eye and easier to understand.
Yield and scope in the modern switch

Let us introduce a small change in the business requirement. If we have a budget of 3 million or 5 million we need to give the customer a discount.Doing this using the vintage switch would lead to the following code:

public int getNumberOfRooms(int budget) {
        int numberOfRooms = 0;
        switch (budget) {
            case 1000000:
                numberOfRooms = 1;
                break;
            case 2000000:
            case 3000000:
                int finalPrice = (budget == 3000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);
                numberOfRooms = 2;
                break;
            case 4000000:
            case 5000000:
                int finalPriceThreeRooms = (budget == 5000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPriceThreeRooms);
                numberOfRooms = 3;
                break;
            default:
                throw new IllegalStateException("No rooms available for this budget");
        }
        return numberOfRooms;
}

If you see line number 9 and line number 15 , we introduced 2 variables which does the same thing but we had to give it different names. This is because any variable introduced in case block stretches for the entire switch block.

Let us implement this using modern switch construct

public int getNumberOfRooms(int budget) {
        int numberOfRooms = switch (budget) {
            case 1000000 -> 1;
            case 2000000, 3000000 -> {
                int finalPrice = (budget == 3000000) ? budget - 100000 : budget);
                System.out.println("Final price after discount " + finalPrice);

                yield 2;
            }
            case 4000000, 5000000 -> {
                int finalPrice = (budget == 5000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);

                yield 3;
            }
            default -> throw new IllegalStateException("Unexpected value: " + budget);
        };
        return numberOfRooms;
}

Using the modern switch, if a case block has multiple lines, we make use of a yield statement which indicates that the case block yields a value. Besides this, another salient feature is that the finalPrice variable introduced inside the case block remains local to that block which makes reading and understanding code easier. I have seen lots of code where variables are named using weird combinations like temp1, temp2, temp3 or finalPrice1,finalPrice2,finalPrice3 …This change does not enforce you to give meaningful names to variables but signals a clear intention about the scope of a variable in the case block.

More on the yield statement

The yield statement was introduced after seeking feedback. In JDK 12, break statement was used in switch expression which was confusing. It was hence decided to introduce the yield statement in switch expressions. I have been calling the switch in versions of JDK prior to 12 as vintage but they continue to exist along with the modern switch.

Some nuances with mixing and matching
  • We cannot use the arrow and the colon in the same switch block. We get a compile time error “Different case kinds used in switch”
public int getNumberOfRooms(int budget) {
int numberOfRooms = 0 ;
switch (budget) {
case 1000000 : numberOfRooms = 1;
case 2000000,3000000 -> {
  …
}
case 4000000, 5000000 -> { 
  …
}
default -> throw new IllegalStateException("Unexpected value: " + budget);
};
return numberOfRooms;
}
  • In case of the switch statement, we can use the new case label using the arrow. Doing this means there is no fall through and multiple cases can be combined using comma. You can add a break statement( no compiler error) but the arrow case label means there is no fall through
public int getNumberOfRooms(int budget) {
        int numberOfRooms;
        switch (budget) {
            case 1000000 -> {
                numberOfRooms = 1;
            }
            case 2000000, 3000000 -> {
                int finalPrice = (budget == 3000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);
                numberOfRooms = 2;
                //redundant because of arrow.
                break;
            }
            case 4000000, 5000000 -> {
                int finalPrice = (budget == 5000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);
                numberOfRooms = 3;
            }
            default -> throw new IllegalStateException("Unexpected value: " + budget);
        }
        return numberOfRooms;
    }
  • In case of a switch expression, we can use the old vintage style of using a colon for the case blocks. But since this is an expression, each case block must return a value using yield.
public int getNumberOfRooms(int budget) {
        int numberOfRooms = switch (budget) {
            case 1000000: {
                yield 1;
            }
            case 2000000, 3000000: {
                int finalPrice = (budget == 3000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);
                yield 2;
            }
            case 4000000, 5000000: {
                int finalPrice = (budget == 5000000) ? budget - 100000 : budget;
                System.out.println("Final price after discount " + finalPrice);
                yield 3;
            }
            default:
                throw new IllegalStateException("Unexpected value: " + budget);
        };
        return numberOfRooms;
    }

Conclusion

The new switch expression feature will make our code more readable, intuitive and less error prone. This feature acts as a foundation for more changes to come in the Java language, one of them is pattern matching.

References

https://openjdk.java.net/jeps/361