Anonymous classes traditionally enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. With lambda expressions introduced in Java 8 we can move anonymous classes to lambda expressions and with that we will have less code, clean code and more assertive functionality definition. This time I will show you how to transfrom a anonymous class to lambda expression in two steps.
First let’s create a simple java project using Gradle, in order to do that, in your root project create a build.gradle
file with this structure:
apply plugin: "java"
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-api:5.2.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
}
test {
useJUnitPlatform()
reports {
html.enabled = true
}
}
We are going to use JUnit 5 in order to create test and validate we are building the right functionality. Now it is time to create a Functional Interface, in Java is any with @FunctionalInterface
annotation and with SAM(Single Abstract Method). It was introduced to facilitate Lambda expressions.
package com.jos.dem.anonymous;
@FunctionalInterface
public interface Greeting {
String hello();
}
Anonymous classes are unnamed instances of an interface or base class that are defined and instantiated with a single new command.
@Test
@DisplayName("Should show get message as anonymous class")
void shouldGetMessageWithAnonymous() {
final String message = greetingService.call(new Greeting() {
@Override
public String hello() {
return "Hello World!";
}
});
assertEquals("Hello World!", message);
}
Here is our GreetingService class:
package com.jos.dem.anonymous;
public interface GreetingService {
String call(final Greeting greeting);
}
And a HelloWold
greeting service implementation:
package com.jos.dem.anonymous;
public class HelloWorld implements GreetingService {
@Override
public String call(Greeting greeting){
return greeting.hello();
}
}
So the first step to change anonymous class to lambda expression is to get rid of method name:
@Test
@DisplayName("Should remove method name")
void shouldGetMessageWithoutMethodName() {
final String message = greetingService.call(() -> {
return "Hello World!";
});
assertEquals("Hello World!", message);
}
Second step is to remove return statement
@Test
@DisplayName("Should remove return statement")
void shouldGetMessageWithoutReturnStatement() {
final String message = greetingService.call(() -> "Hello World!");
assertEquals("Hello World!", message);
}
Here is the complete test case:
package com.jos.dem.anonymous;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
public class GreetingTest {
private GreetingService greetingService = new HelloWorld();
@Test
@DisplayName("Should show get message as anonymous class")
void shouldGetMessageWithAnonymous() {
final String message = greetingService.call(new Greeting() {
@Override
public String hello() {
return "Hello World!";
}
});
assertEquals("Hello World!", message);
}
@Test
@DisplayName("Should remove method name")
void shouldGetMessageWithoutMethodName() {
final String message = greetingService.call(() -> {
return "Hello World!";
});
assertEquals("Hello World!", message);
}
@Test
@DisplayName("Should remove return statement")
void shouldGetMessageWithoutReturnStatement() {
final String message = greetingService.call(() -> "Hello World!");
assertEquals("Hello World!", message);
}
}
If you want to run this project using Maven, add this file instead of build.gradle
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jos.dem</groupId>
<artifactId>junit5</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
<junit.jupiter.version>5.2.0</junit.jupiter.version>
<junit.platform.version>1.2.0</junit.platform.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- JUnit 5 requires Surefire version 2.22.0 or higher -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
</project>
To browse the project go here, to download the code:
git clone https://github.com/josdem/anonymous-to-lambda-workshop.git
To run the project using Gradle:
gradle test
To run the project using Maven:
mvn test