Spread the Love for Java

Never Stop Learning

Day2DayJava
Lambda Expression Syntax.

Java Lambda Expressions

Key Take Aways:

By reading through this article you would get to know about

  • What are Java lambda expressions?
  • How to write Java lambda expressions?
  • At what all places you can use the java lambda expressions?
  • What are some of the very useful Java functional interfaces?

And if you practice by following the unit tests I have written to explain various concepts related to interfaces in this post, you would also get some basic knowledge of utilizing Mockito to mock various situations for your unit testing.

Pre-Requisites

This article assumes that you have basic knowledge of Java programming language prior to Java version 8 and have a basic understanding of Object Oriented Programming Concepts.

Technologies Referred Here

  • Java programming language version 14.0
  • JUnit version 5.6.2
  • Mockito version 3.3.3
  • Eclipse IDE for Enterprise Java Developers version: 2020-06

Lambda (λ)

A lambda simply means a function without a name. This is a function that you don’t have to declare, define and implement ahead of its use but you can do all that on the fly when you need it.

Java Lambda Expressions

Java lambda expression is basically a block of code that performs a desired function without a name being given to it. Its a one time use block of code that is only relevant or useful in a particular context. Although you can reuse this block of code by assigning it to a suitable variable in the context where it is defined or by passing it to a method accepting a suitable argument type. Suitable variable or argument type for accepting a lambda expression is a Functional Interface.

Syntax of Lambda Expressions

Lambda Expression Syntax.
Lambda Expression Syntax

To understand the syntax of Lambda Expressions let’s take some examples

Lambda Expressions with no Arguments

Lambda Expression with no argument.
Lambda Expression with no argument.

To understand the lambda expression with no argument let’s create a functional interface with a single abstract method that takes no argument and doesn’t return any value.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions with no			* 
 * arguments.															*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface RandomNumberPrinter {

	/**
	 * Prints a random number. 
	 */
	void printRandomNumber();
}

And Let’s create one more functional interface that has a method returning a value.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions taking no		* 
 * argument and returning a value.										*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface UserGreeter {
	/**
	 * Greets every user that visits Day2DayJava.com.
	 * @return
	 */
	String greetUser();
}

Let’s see how we can use a lambda expression to provide an implementation for these interface with a JUnit test. We will also see how we can use similar lambda expression while working with the Java API classes to give us a better idea.

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.Assert.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding Lambda Expressions with no arguments.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class LambdaExpressionsWithNoArgumentTests {

	private final ByteArrayOutputStream outputContent = new ByteArrayOutputStream();
	private final PrintStream originalOutputStream = System.out;

	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outputContent));
	}

	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOutputStream);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.RandomNumberPrinter#printRandomNumber()}.
	 */
	@Test
	@Order(1)
	final void testLambdaAcceptingNoArgument() {

		RandomNumberPrinter randomNumberPrinter = () -> System.out
				.print(String.format("Your random number is \"%s\".", Math.random()));

		randomNumberPrinter.printRandomNumber();

		assertFalse(outputContent.toString().isEmpty());
	}

	/**
	 * Test method for explaining the use of lambda expression with no argument with Java API.
	 */
	@Test
	@Order(2)
	final void testLambdaAcceptingNoArgumentJavaAPI() throws Exception {
		Thread executorThread = new Thread(() -> System.out.print("Runnable Method Called by the Executor Thread."));
		executorThread.start();
		executorThread.join();
		assertEquals(outputContent.toString(), "Runnable Method Called by the Executor Thread.");
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.UserGreeter#greetUser()}.
	 */
	@Test
	@Order(3)
	final void testLambdaAcceptingNoArgumentReturningValue() {

		UserGreeter userGreeter = () -> "Welcome to Day2DayJava.";

		assertEquals("Welcome to Day2DayJava.", userGreeter.greetUser());
	}

	/**
	 * Test method for explaining the use of lambda expression with no argument and returning a value with Java API.
	 */
	@Test
	@Order(4)
	final void testLambdaAcceptingNoArgumentReturningValueJavaAPI() throws Exception {
		ExecutorService executorService = Executors.newSingleThreadExecutor();

		Future<String> resultFuture = executorService.submit(() -> "Callable Method called by the Executor Service.");

		assertEquals("Callable Method called by the Executor Service.", resultFuture.get());
	}
}

I have highlighted all the lines in the above test class where we are using a lambda expression with no arguments. Let’s first run these tests and see an all green result. Then we will go test case by test case to understand how those lambdas are working in our test methods.

Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes no arguments.
Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes no arguments.

testLambdaAcceptingNoArgument(): In this test we can see that “randomNumberPrinter” variable has been assigned a lambda expression. The lambda expression here is providing an implementation of the RandomNumberPrinter interface’s method “printRandomNumber()”. That is later invoked by the test. And we can see that it prints the following output to the output stream by putting a debug point in the test and inspecting the outputContent variable’s value for the first test.

String value written to the output stream.
String value written to the output stream.

testLambdaAcceptingNoArgumentJavaAPI(): The lambda expression present on line number 66 is providing an implementation for Java’s Runnable interface’s run() method. Java compiler understands and treats a lambda expression as an implementation of a particular functional interface’s method smartly based on the lambda expression’s context and assignment. It is similar to writing an anonymous class implementation in prior to Java version 8 but in a more compact way.

testLambdaAcceptingNoArgumentReturningValue(): Tests a lambda expression that returns a value but still doesn’t take an argument. The lambda expression present in this test case at line 79 returns a String value. Notice how we don’t even have to put a return keyword. Java compiler determines that based on the context in which that lambda expression is used and considers the string message present there as the return value from our lambda. In this case compiler determines that this lambda expression is providing an implementation for the greetUser() method from the UserGreeter interface. The assert present in the test case validates that on invoking the greetUser() method it returns the greeting message provided by the lambda.

testLambdaAcceptingNoArgumentReturningValueJavaAPI(): The lambda expression present on line number 92 in this case is providing an implementation for Java’s Callable interface’s call() method. Notice how in this case Java compiler understood and treated our lambda expression as an implementation of the Callable and not Runnable interface. Java compiler does that smartly based on the lambda expression’s context and assignment. As in this case our lambda expression is returning a value.

Lambda Expressions with one Argument

Syntax for lambda expression with one argument.
Syntax for lambda expression with one argument.

To understand the lambda expression with one argument let’s create a functional interface with a single abstract method that takes only one argument and doesn’t return any value.

package com.day2dayjava.tutorials.java.lambda.expressions;

import java.util.Collection;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions with one			* 
 * argument.															*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface EmptyAndNullElementFilter {

	/**
	 * This method is going to remove all the null and empty elements if present in the passed collection.
	 * 
	 * @param collection that may have null or empty string values.
	 */
	void removeEmptyAndNullElements(Collection<String> collection);
}

And Let’s create one more functional interface that has a method returning a value.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions accepting one	* 
 * argument and returning a value.										*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface OddValueIdentifier {

	/**
	 * Tells whether it is a odd number or not.
	 * 
	 * @param number to check for whether it is an odd number or even.
	 * @return true/false
	 */
	boolean isOddInteger(Integer number);

}

Let’s see how we can use a lambda expression to provide an implementation for these interface with a JUnit test. We will also see how we can use similar lambda expression while working with the Java API classes to give us a better idea.

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding Lambda Expressions with one argument.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class LambdaExpressionsWithOneArgumentTests {

	private final ByteArrayOutputStream outputContent = new ByteArrayOutputStream();
	private final PrintStream originalOutputStream = System.out;

	/**
	 * By default System.out writes to the console, set the output stream to our own output stream. 
	 * This will enables us to read it if needed for validating the output written by the System.out.
	 */
	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outputContent));
	}

	/**
	 * After the test is complete set output stream for System.out back to the default stream.
	 */
	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOutputStream);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.EmptyAndNullElementFilter#removeEmptyAndNullElements(java.util.Collection)}.
	 */
	@Test
	@Order(1)
	final void testLambdaAcceptingOneArgument() {
		List<String> unFilteredData = new ArrayList<>(Arrays.asList("One", null, "Two", "Three", "Four", null, "Five"));

		assertEquals(7, unFilteredData.size());

		EmptyAndNullElementFilter emptyAndNullElementFilter = (collection) -> {
			if (collection != null && !collection.isEmpty()) {
				Iterator<String> iterator = collection.iterator();
				while (iterator.hasNext()) {
					String element = iterator.next();
					if (element == null || element.trim().isEmpty()) {
						iterator.remove();
					}
				}
			}
		};

		emptyAndNullElementFilter.removeEmptyAndNullElements(unFilteredData);

		assertEquals(5, unFilteredData.size());
	}

	/**
	 * Shows the use of a lambda expression accepting one argument with the Java API.
	 */
	@Test
	@Order(2)
	final void testLambdaAcceptingOneArgumentJavaAPI() {
		List<Integer> numbersUptoFive = List.of(1, 2, 3, 4, 5);

		numbersUptoFive.forEach(number -> System.out.print(number));

		assertEquals("12345", outputContent.toString());
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.OddValueIdentifier#isOddInteger(Integer)}.
	 */
	@Test
	@Order(3)
	final void testLambdaAcceptingOneArgumentReturningValue() {
		OddValueIdentifier oddValueIdentifier = valueToValidate -> valueToValidate % 2 == 1;

		assertTrue(oddValueIdentifier.isOddInteger(7));
		assertFalse(oddValueIdentifier.isOddInteger(6));
	}

	/**
	 * Shows the use of a lambda expression accepting one argument and returning a value with the Java API.
	 */
	@Test
	@Order(4)
	final void testLambdaAcceptingOneArgumentReturningValueJavaAPI() {
		List<String> unFilteredData = new ArrayList<>(Arrays.asList("One", null, "Two", "Three", "Four", null, "Five"));

		assertEquals(7, unFilteredData.size());

		unFilteredData = unFilteredData.stream().filter(element -> element != null && !element.trim().isEmpty())
				.collect(Collectors.toList());

		assertEquals(5, unFilteredData.size());
	}
}

I have highlighted all the relevant lines in the above test class where we are using a lambda expression with one argument. Let’s run these tests and see the results. Then we will go test case by test case to understand how those lambdas are working in our test methods.

Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes one argument.
Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes one argument.

testLambdaAcceptingOneArgument(): In this test we can see that “emptyAndNullElementFilter” variable has been assigned a lambda expression. The lambda expression here is providing an implementation of the EmptyAndNullElementFilter interface’s method “removeEmptyAndNullElements()”. We can see here that lambda expression doesn’t always have to be a single statement. In this use case lines from 65-75 represent the lambda implementation. This lambda accepts a single argument in this case represented by the name “collection”.

testLambdaAcceptingOneArgumentJavaAPI(): The lambda expression in this use case is taking an argument “number” which is actually an element coming from the collection “numbersUptoFive” and is being used by the lambda implementation. The lambda implementation is to just print that number into the output stream.

testLambdaAcceptingOneArgumentReturningValue(): The lambda expression implementation in this case takes an integer argument and returns a boolean value. Note that as long as it is a single statement we do not need to enclose the statement in braces and as long as the operation happening in that lambda expression is not resulting into a void result we do not need to explicitly put a return statement.

testLambdaAcceptingOneArgumentReturningValueJavaAPI(): In this test case we can see that the lambda expression works as a predicate. It takes an argument which is an element of the “unFilteredData” list and returns a boolean value after applying the specified condition on that element.

Lambda Expressions with two Arguments

Syntax for lambda expression with two arguments.
Syntax for lambda expression with two arguments.

To understand the lambda expression with two arguments let’s create a functional interface with a single abstract method that takes only two arguments and doesn’t return any value.

package com.day2dayjava.tutorials.java.lambda.expressions;

import java.time.ZonedDateTime;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions with two			* 
 * arguments.															*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface TimeBasedUserGreetor {

	/**
	 * Greets a user based on the time of the day.
	 * 
	 * @param userName user name to greet with.
	 * @param dateAndTime date and time to identify the time of the day.
	 */
	void greetUser(String userName, ZonedDateTime dateAndTime);
}

And Let’s create one more functional interface that has a method returning a value.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions taking two 	 	* 
 * arguments and returning a value.										*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface FullUserNameProducer {

	/**
	 * This method should concatenate the first and last name.
	 * @param firstName first name of the user.
	 * @param lastName last name of the user.
	 * @return combined full name of the user.
	 */
	String getFullUserName(String firstName, String lastName);
}

Let’s see how we can use a lambda expression to provide an implementation for these interface with a JUnit test. And we will also see how we can use similar lambda expression while working with the Java API classes to give us a better idea.

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding Lambda Expressions with two arguments.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class LambdaExpressionsWithTwoArgumentsTests {

	private final ByteArrayOutputStream outputContent = new ByteArrayOutputStream();
	private final PrintStream originalOutputStream = System.out;

	/**
	 * By default System.out writes to the console, set the output stream to our own output stream. 
	 * This will enables us to read it if needed for validating the output written by the System.out.
	 */
	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outputContent));
	}

	/**
	 * After the test is complete set output stream for System.out back to the default stream.
	 */
	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOutputStream);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.TimeBasedUserGreetor#greetUser(String, java.time.ZonedDateTime)}.
	 */
	@Test
	@Order(1)
	final void testLambdaAcceptingTwoArguments() {
		TimeBasedUserGreetor timeBasedUserGreetor = (userName, dateAndTime) -> {
			Integer hourOfTheDay = -1;
			if (dateAndTime != null) {
				hourOfTheDay = dateAndTime.getHour();
			}
			if (userName == null || userName.trim().isEmpty()) {
				userName = "Guest";
			}
			if (hourOfTheDay < 12) {
				System.out.print(String.format("Good Morning %s", userName));
			} else if (hourOfTheDay < 16) {
				System.out.print(String.format("Good Afternoon %s", userName));
			} else if (hourOfTheDay < 23) {
				System.out.print(String.format("Good Evening %s", userName));
			} else {
				System.out.print(String.format("Welcome %s", userName));
			}
		};
		String userName = "John";
		LocalDateTime localDateTime = LocalDateTime.of(2020, Month.JULY, 25, 16, 30);
		ZonedDateTime californiaTimeOfTheDay = ZonedDateTime.of(localDateTime, ZoneId.of("America/Los_Angeles"));

		timeBasedUserGreetor.greetUser(userName, californiaTimeOfTheDay);

		assertEquals(String.format("Good Evening %s", userName), outputContent.toString());
	}

	/**
	 * Shows the use of a lambda expression accepting two arguments with the Java API.
	 */
	@Test
	@Order(2)
	final void testLambdaAcceptingTwoArgumentsJavaAPI() {

		Map<String, String> famousSportsPersons = new LinkedHashMap<>();
		famousSportsPersons.put("Muhammad", "Ali");
		famousSportsPersons.put("Roger", "Federer");
		famousSportsPersons.put("Michael", "Schumacher");
		famousSportsPersons.put("Lance", "Armstrong");
		famousSportsPersons.put("Usain", "Bolt");

		famousSportsPersons.forEach((k, v) -> System.out.print(k + " " + v + ", "));

		assertEquals("Muhammad Ali, Roger Federer, Michael Schumacher, Lance Armstrong, Usain Bolt, ",
				outputContent.toString());
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.FullUserNameProducer#getFullUserName(String, String)}.
	 */
	@Test
	@Order(3)
	final void testLambdaAcceptingTwoArgumentsReturningValue() {
		FullUserNameProducer fullUserNameProducer = (firstName, lastName) -> String.format("%s %s", firstName,
				lastName);

		assertEquals("Michael Phelps", fullUserNameProducer.getFullUserName("Michael", "Phelps"));
	}

	/**
	 * Shows the use of a lambda expression accepting two arguments and returning a value with the Java API.
	 */
	@Test
	@Order(4)
	final void testLambdaAcceptingTwoArgumentsReturningValueJavaAPI() {
		List<Integer> unsortedData = new ArrayList<>(Arrays.asList(9, 7, 1, 5, 4, 3, 0));
		Collections.sort(unsortedData, (valueOne, valueTwo) -> valueOne.compareTo(valueTwo));

		//The unsortedData should be sorted now.
		assertEquals(0, unsortedData.get(0));
		assertEquals(1, unsortedData.get(1));
		assertEquals(3, unsortedData.get(2));
		assertEquals(4, unsortedData.get(3));
		assertEquals(5, unsortedData.get(4));
		assertEquals(7, unsortedData.get(5));
		assertEquals(9, unsortedData.get(6));
	}
}

The highlighted lines in the above test class are the lines where we are using a lambda expression with two argument. Let’s run these tests and see the results. Then we will go test case by test case to understand how those lambdas are working in our test methods.

Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes two arguments.
Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes two arguments.

testLambdaAcceptingTwoArguments(): This test case explains the lambda expression that takes two arguments. We can see the lambda implementation from highlighted lines, again it is a multiline lambda expression. This lambda implementation is to greet a user based on the time of the day. The test case validates the implementation by comparing the expected result with the content of output stream.

testLambdaAcceptingTwoArgumentsJavaAPI(): Here we can see that the “famousSportsPersons.foreach(…)” accepts a lambda implementation that takes two arguments and consume them. In this case the lambda expression is taking the key and value of each Map entry and utilizing them by printing them to the output stream. basically concatenating the first name and last name of famous sports persons.

testLambdaAcceptingTwoArgumentsReturningValue(): The highlighted lambda expression in this case is taking two arguments and returning a value(and not just printing). In this case it is providing an implementation of our “FullUserNameProducer” interface’s “getFullUserName” method.

testLambdaAcceptingTwoArgumentsReturningValueJavaAPI(): The highlighted lambda expression in this use case is providing an implementation of the Java’s Comparator interface, that takes two arguments to compare them and returns their comparison score (+ve value, 0, or -ve value).

Lambda Expressions with explicit Arguments

We may not need to ever provide an explicit type for the arguments of a lambda expression. It’s up to you whether you want to put explicit argument types or not. Sometimes for better code readability or clarity you may want to put the explicit argument type. For example let’s take a simple example of number subtraction provided by a “MathOperationsExecutor” class. Let’s first create an interface that has a subtraction method for the integer numbers.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions accepting two	*
 * argument and also specifying explicit argument type.					*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface IntegerSubtractor {
	/**
	 * Does the integer subtraction.
	 * @param subtractFrom subtract from this number.
	 * @param subtractThis subtract this number.
	 * @return the remaining value post subtraction.
	 */
	int subtract(int subtractFrom, int subtractThis);
}

And create another interface that provides a subtraction feature for long numbers.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions accepting two	*
 * argument and also specifying explicit argument type.					*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface LongSubtractor {
	/**
	 * Does the long subtraction.
	 * @param subtractFrom subtract from this number.
	 * @param subtractThis subtract this number.
	 * @return the remaining value post subtraction.
	 */
	long subtract(long subtractFrom, long subtractThis);
}

Now let’s create a class that uses both of these interfaces to provide the subtraction feature for both integer and long numbers.

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions accepting two	*
 * argument and also specifying explicit argument type.					*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public class MathOperationsExecutor {
	/**
	 * Interface that performs integer subtraction.
	 */
	IntegerSubtractor integerSubtractor = null;
	/**
	 * Interface that performs long subtraction.
	 */
	LongSubtractor longSubtractor = null;

	/**
	 * Default constructor that takes a <code>IntegerSubtractor</code> and <code>LongSubtractor</code>
	 * as arguments.
	 * 
	 * @param integerSubtractor the instance of a class(or a lambda implementation) 
	 * that performs the long subtraction.
	 * @param longSubtractor the instance of a class(or a lambda implementation) 
	 * that performs the long subtraction.
	 */
	public MathOperationsExecutor(IntegerSubtractor integerSubtractor, LongSubtractor longSubtractor) {
		this.integerSubtractor = integerSubtractor;
		this.longSubtractor = longSubtractor;
	}

	/**
	 * Performs integer subtraction.
	 * @param subtractFrom subtract from this number.
	 * @param subtractThis subtract this number.
	 * @return the remaining value post subtraction.
	 */
	public int executeSubtraction(int subtractFrom, int subtractThis) {
		return integerSubtractor.subtract(subtractFrom, subtractThis);
	}

	/**
	 * Performs long subtraction.
	 * @param subtractFrom subtract from this number.
	 * @param subtractThis subtract this number.
	 * @return the remaining value post subtraction.
	 */
	public long executeSubtraction(long subtractFrom, long subtractThis) {
		return longSubtractor.subtract(subtractFrom, subtractThis);
	}
}

Now let’s write a JUnit test case to test the features provided by this class.

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding a use case where we might want to explicitly *
 * specify the argument type											*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class LambdaExpressionsWithTwoArgumentsExplicitTypeSpecificationTest {

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.MathOperationsExecutor#MathOperationsExecutor(com.day2dayjava.tutorials.java.lambda.expressions.IntegerSubtractor, com.day2dayjava.tutorials.java.lambda.expressions.LongSubtractor)}.
	 */
	@Test
	final void testMathOperationsExecutor() {
		//@formatter:off
		MathOperationsExecutor mathOperationsExecutor = new MathOperationsExecutor(
				(int subtractFrom, int subtractThis) -> {return subtractFrom - subtractThis;},
				(long subtractFrom, long subtractThis) -> {return subtractFrom - subtractThis;}
				);
		//@formatter:on
		assertEquals(5, mathOperationsExecutor.executeSubtraction(10, 5));
		assertEquals(5l, mathOperationsExecutor.executeSubtraction(10l, 5l));
	}

}

Let’s run this test and see the results first.

Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes two arguments with explicit type specified.
Results of Running the JUnit test cases in eclipse for verifying the Lambda Implementations that takes two arguments with explicit type specified.

As you may see here we could have very well defined the lambda expressions without explicit argument type as well but that might not have been as clear to read or tell which interface implementation is being provided by each lambda expression as shown below.

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding a use case where we might want to explicitly *
 * specify the argument type											*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class LambdaExpressionsWithTwoArgumentsExplicitTypeSpecificationTest {

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.MathOperationsExecutor#MathOperationsExecutor(com.day2dayjava.tutorials.java.lambda.expressions.IntegerSubtractor, com.day2dayjava.tutorials.java.lambda.expressions.LongSubtractor)}.
	 */
	@Test
	final void testMathOperationsExecutor() {
		//@formatter:off
		MathOperationsExecutor mathOperationsExecutor = new MathOperationsExecutor(
				(subtractFrom, subtractThis) -> {return subtractFrom - subtractThis;},
				(subtractFrom, subtractThis) -> {return subtractFrom - subtractThis;}
				);
		//@formatter:on
		assertEquals(5, mathOperationsExecutor.executeSubtraction(10, 5));
		assertEquals(5l, mathOperationsExecutor.executeSubtraction(10l, 5l));
	}

}

But note that you can either specify the argument type for all the parameters or for none of them but you can not specify for some and not specify for some.

Lambda Expressions with multiple statements

We have already seen few examples of lambdas with multiple statements above so nothing much to explain here. For example we had the lambda implementations having multiple statements in the body at the following places above.

Lambda Expression Usage

A lambda expression can be used anywhere in the Java code where the compiler can ascertain the target type to which the lambda expression is being assigned. For example it can be used at the following places:

  • Variable declarations
  • Assignments.
  • Return statements
  • Array initializers
  • Method or constructor arguments.
  • Lambda expression bodies
  • Conditional Expressions, ?:
  • Cast Expressions

We saw one example of constructor arguments in our example for lambda expressions with explicit arguments.

Lambda Expression’s use of enclosing scope variables

Lambda expressions can use the variables(or constants) defined in it’s enclosing scope directly, as if the code inside the lambda expression was present in the enclosing scope itself and was not in the lambda body. That is because the lambdas do not introduce a new level of scope. So they can access the method parameters, class fields etc. that are available to it’s enclosing scope the same way that the enclosing scope can. Only difference is that you can not use a variable that is not final or at least “effectively final” before it’s use in the lambda expression i.e. any variable used in a lambda expression from it’s enclosing scope should not be reassigned a value post it’s declaration in the enclosing scope before it’s use in the lambda. Otherwise the Java compiler would throw an exception. Let’s create an interface that we will use for explaining the lambda expression’s access of enclosing scope variables:

package com.day2dayjava.tutorials.java.lambda.expressions;

/**
 ************************************************************************
 * Example interface to explain the lambda expressions using a variable	* 
 * from it's enclosing scope.											*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface OneToTenTablePrinter {

	/**
	 * Prints the mathematical table for a given number(picks it up from the enclosing scope).
	 */
	void printTable();
}

Now let’s write a JUnit test case to understand the lambda expression’s access of enclosing scope variables:

package com.day2dayjava.tutorials.java.lambda.expressions;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests for understanding Lambda expression's access to the enclosing 	*
 * scope.																*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class LambdaExpressionScopeTest {

	private final ByteArrayOutputStream outputContent = new ByteArrayOutputStream();
	private final PrintStream originalOutputStream = System.out;

	/**
	 * By default System.out writes to the console, set the output stream to our own output stream. 
	 * This will enables us to read it if needed for validating the output written by the System.out.
	 */
	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outputContent));
	}

	/**
	 * After the test is complete set output stream for System.out back to the default stream.
	 */
	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOutputStream);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.lambda.expressions.OneToTenTablePrinter#printTable()}.
	 */
	@Test
	final void testPrintTable() {
		int inputNumber = 2;
		//inputNumber = 6;
		OneToTenTablePrinter tablePrinter = () -> {
			//int inputNumber = 8;
			for (int i = 1; i <= 10; i++) {
				System.out.print((inputNumber * i) + " ");
			}
		};
		tablePrinter.printTable();
		assertEquals("2 4 6 8 10 12 14 16 18 20 ", outputContent.toString());
	}
}

Let’s run this test case and see the successful results.

Result of running the "testPrintTable()" method for validating the Lambda Expression's use of variables from the enclosing scope.
Result of running the “testPrintTable()” method for validating the Lambda Expression’s use of variables from the enclosing scope.

You can see that the moment you uncomment the line 51, compiler starts throwing an error telling you that the variable used by the lambda expression is not “effectively final” as shown below.

Compiler error on uncommenting the "inputNumber=6" statement.
Compiler error on uncommenting the “inputNumber=6;” statement.

Same way you can also see that the compiler would again start crying if you try to use the same variable name as was used by its enclosing scope by uncommenting the line with statement “int inputNumber =8;”.

Compiler error on uncommenting the "int inputNumber = 8;" statement.
Compiler error on uncommenting the “int inputNumber = 8;” statement.

Functional Interfaces in Java API

Many of the functional interfaces we created in our examples above we don’t actually have to create. As all we are doing with them is using them as a receptor or target type for the lambda expressions. Java JDK already comes with a lot of useful functional interfaces that we can use as target types for these lambda expressions.

Functional Interfaces present in JDK 14 API

We will take a look at some of these in our next post.

Source Download

You can download full eclipse project from my Github repository using the following link: https://github.com/Day2DayJava/java

What’s Next

Next we will learn about the Java Functional Interfaces and see few examples of utilizing them.

Related Posts

All the Java Interfaces and Classes used for explaining the concept of Java Interfaces and its use in this post.

Java Interfaces

Key Take Aways:

By reading through this article you would get to know about

  • What are Java Interfaces?
  • Know about type of methods a Java Interface could have.
  • Why do Java Interfaces exists if we have abstract classes?

And if you practice by following the unit tests I have written to explain various concepts related to interfaces in this post, you would also get some basic knowledge of utilizing Mockito to mock various situations for your unit testing.

Pre-Requisites

This article assumes that you have some basic knowledge of Java language and have a basic understanding of Object Oriented Programming Concepts.

Technologies Referred Here

  • Java programming language version 14.0
  • JUnit version 5.6.2
  • Mockito version 3.3.3
  • Eclipse IDE for Enterprise Java Developers version: 2020-06

Interfaces

Let’s first understand what an interface is. An interface is a way to interact with something. In our day to day life we get to interact with a lot of interfaces that makes our life easy. For example

  • A light switch allows us to power on or off a light.
  • A car accelerator allows us to operate the car speed.
  • A thermostat allows us to control the room temperature.

So how does an interface makes our life easy, that is because an interface

  • Only exposes the most important function to the outside world hiding all the non essential information and complexity behind it. For example a thermostat provides you an interface to set the room temperature. But you don’t need to know how it controls and maintain the room temperature, it hides behind all the logic and implementation details for temperature control.
  • Once you learn to operate an interface, you can operate anything that implements or provides the same interface to operate it. For example once you learn driving a car, you could drive any car that has the same interface as the one you learnt. So it promotes standardization.

Example Interfaces & Class Diagram

Before we start diving in, here is a diagram depicting all the Java Interfaces and Classes used for explaining the concept in this post.

A Diagram depicting all the Java Interfaces and Classes used for explaining the concept in this post.
All the Java Interfaces and Classes used for explaining the concept in this post.

Java Interfaces

A Java interface is a way to achieve abstraction in the Java Language. It is just like a class but having only method signatures and no body. An interface could have

  • Member attributes that are by default constant i.e. public, static and final
  • Member methods that are by default abstract and public

Here is an example of a Java Interface:

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * An interface to represent Vehicles. This interface has just methods. *
 * 																		*	
 * @author D2DJ 														*
 ************************************************************************
 **/
public interface Vehicle {
	//Returns the make/brand name of the vehicle manufacturer.
	String getMake();

	// Returns the make year of the vehicle.
	Integer getMakeYear();

	// Returns the Vehicle Model
	String getModel();

	//Returns the detailed specification of the Vehicle.
	String getConfiguration();

	//Returns the base price of the vehicle that goes out of the box 
	//without any customizations.
	BigDecimal getBasePrice();

}

Since Java 8 you can also add default methods with body and static methods with body too, those are discussed later in this post.

A java interface supports inheritance and could extend one or more java interfaces. For example we could create an interface for Motorcycles that extends the interface Vehicle.

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * An interface extending another interface. This interface represents  *
 * all Vehicles that represent the Vehicle Category Motorcycle.  		*
 *																		* 
 * @author D2DJ 														*
 ************************************************************************
 **/
public interface Motorcycle extends Vehicle {
	enum MotorCycleStyle {
		STANDARD, CRUISER, SPORT_BIKE, SCOOTER, OFF_ROAD, TOURING
	}

	//Returns the body style of the car. For example one of Standard, Cruiser, 
	//Sport Bike, Scooter, Off-road, Touring etc.
	MotorCycleStyle getMotorCycleStyle();
}

And let’s create another interface Car that extends Vehicle interface too.

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * Example for an interface extending another interface. This interface *
 * represents all Vehicles that represent the Vehicle Category Car.     *
 *                                                                      *
 * @author D2DJ                                                         *
 ************************************************************************
 **/
public interface Car extends Vehicle {
	enum CarStyle {
		COUPE, CONVERTIBLE, SEDAN, WAGON, HATCHBACK, SUV
	}

	/**
	 * Member attributes, that are a constant.
	 **/
	int MIN_DOORS = 1;
	int MAX_DOORS = 5;

	//Returns the body style of the car. For example one of Coupe, Convertible,
	//Sedan, Wagon, Hatchback, SUV etc.
	CarStyle getCarStyle();
}

Here is a diagram showing this inheritance between the above interfaces:

Image showing Inheritance between interfaces.
Inheritance between interfaces

Java interface also provides another way to achieve polymorphism. For example let’s create some classes implementing these two interfaces.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * An abstract class providing a default behavior for the common method.*
 * 																		*	
 * @author D2DJ 														*
 ************************************************************************
 **/
public abstract class GroundVehicle implements Vehicle {
	protected String make;
	protected Integer makeYear;
	protected String model;
	protected BigDecimal basePrice;

	/**
	 * @param make the make/brand name of the vehicle manufacturer.
	 * @param makeYear the make year of the vehicle.
	 * @param model Vehicle Model.
	 * @param basePrice Vehicle base price.
	 */
	protected GroundVehicle(String make, Integer makeYear, String model, BigDecimal basePrice) {
		super();
		this.make = make;
		this.makeYear = makeYear;
		this.model = model;
		this.basePrice = basePrice;
	}

	// Returns the make/brand name of the vehicle manufacturer.
	@Override
	public String getMake() {
		return this.make;
	}

	// Returns the make year of the vehicle.
	@Override
	public Integer getMakeYear() {
		return this.makeYear;
	}

	// Returns the Vehicle Model
	@Override
	public String getModel() {
		return this.model;
	}

	@Override
	public BigDecimal getBasePrice() {
		return this.basePrice;
	}

	// Prints the detailed specification of the Vehicle.
	@Override
	public String getConfiguration() {
		return String.format("GroundVehicle [make=%s, makeYear=%s, model=%s]", getMake(), getMakeYear(), getModel());
	}
}
package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * Class representing cars built by Honda.								*
 *																		*
 * @author D2DJ                                                      	*
 ************************************************************************
 **/
public class HondaCar extends GroundVehicle implements Car {

	private CarStyle carStyle = null;

	/**
	 * @param make the make/brand name of the vehicle manufacturer.
	 * @param makeYear the make year of the vehicle.
	 * @param model Vehicle Model
	 * @param basePrice Vehicle base price.
	 * @param carStyle the style of the Car
	 */
	protected HondaCar(String make, Integer makeYear, String model, BigDecimal basePrice, CarStyle carStyle) {
		super(make, makeYear, model, basePrice);
		this.carStyle = carStyle;
	}

	@Override
	public CarStyle getCarStyle() {
		return this.carStyle;
	}

	@Override
	public String getConfiguration() {
		return String.format("HondaCar [carStyle=%s, make=%s, makeYear=%s, model=%s]", getCarStyle(), getMake(),
				getMakeYear(), getModel());
	}
}
package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * Class representing cars built by Honda.								*
 *																		*
 * @author D2DJ														    *
 ************************************************************************
 **/
public class HondaMotorcycle extends GroundVehicle implements Motorcycle {

	private MotorCycleStyle motorCycleStyle = null;

	/**
	 * @param make the make/brand name of the vehicle manufacturer.
	 * @param makeYear the make year of the vehicle.
	 * @param model Vehicle Model
	 * @param basePrice Vehicle base price.
	 * @param motorCycleStyle the style of the Motorcycle.
	 */
	protected HondaMotorcycle(String make, Integer makeYear, String model, BigDecimal basePrice,
			MotorCycleStyle motorCycleStyle) {
		super(make, makeYear, model, basePrice);
		this.motorCycleStyle = motorCycleStyle;
	}

	@Override
	public MotorCycleStyle getMotorCycleStyle() {
		return this.motorCycleStyle;
	}

	@Override
	public String getConfiguration() {
		return String.format("HondaMotorcycle [motorCycleStyle=%s, make=%s, makeYear=%s, model=%s]",
				getMotorCycleStyle(), getMake(), getMakeYear(), getModel());
	}
}

Now we can write a utility class that operates on Vehicle interface and could utilize the polymorphic nature.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;
import java.time.LocalDate;

/**
 ************************************************************************
 * Utility class to calculate a vehicle's current value based on its age*
 * considering that a vehicle's value decreases due to depreciation.	*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public abstract class VehicleDepreciationCalculator {

	private static final int DEPRECIATION_EVERY_YEAR = 500;

	/**
	 * Returns the current vehicle value based upon it's age.
	 * 
	 * @param vehicle the vehicle.
	 * @return current vehicle value.
	 */
	public static final BigDecimal getCurrentVehicleValue(Vehicle vehicle) {
		BigDecimal vehicleAge = BigDecimal.valueOf(LocalDate.now().getYear() - vehicle.getMakeYear());
		BigDecimal depreciationEveryYear = new BigDecimal(DEPRECIATION_EVERY_YEAR);
		BigDecimal vehicleBasePrice = vehicle.getBasePrice();
		BigDecimal currentVehicleValue = vehicleBasePrice.subtract(vehicleAge.multiply(depreciationEveryYear));
		BigDecimal zeroValue = BigDecimal.valueOf(0);
		return currentVehicleValue.compareTo(zeroValue) > 0 ? currentVehicleValue : zeroValue;
	}
}

Let’s test this polymorphic behavior with a JUnit test.

package com.day2dayjava.tutorials.java.interfaces;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import java.math.BigDecimal;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Test class for VehicleDepreciationCalculator.						*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestVehicleDepreciationCalculator {

	@Mock
	private HondaCar hondaCar;
	@Mock
	private HondaMotorcycle hondaMotorcycle;

	/**
	 * Test method for 
	 * {@link com.day2dayjava.tutorials.java.interfaces.VehicleDepreciationCalculator#getCurrentVehicleValue(
	 * com.day2dayjava.tutorials.java.interfaces.Vehicle)}.
	 */
	@Test
	void testGetCurrentVehicleValue() {
		//Setup Honda car Mock
		when(hondaCar.getMakeYear()).thenReturn(2020);
		when(hondaCar.getBasePrice()).thenReturn(BigDecimal.valueOf(30000));

		//Setup Honda Motorcycle Mock
		when(hondaMotorcycle.getMakeYear()).thenReturn(2020);
		when(hondaMotorcycle.getBasePrice()).thenReturn(BigDecimal.valueOf(5000));

		assertEquals(BigDecimal.valueOf(30000), VehicleDepreciationCalculator.getCurrentVehicleValue(hondaCar));
		assertEquals(BigDecimal.valueOf(5000), VehicleDepreciationCalculator.getCurrentVehicleValue(hondaMotorcycle));

		//Let's change the make to 5 years back and test.
		when(hondaCar.getMakeYear()).thenReturn(2015);
		when(hondaMotorcycle.getMakeYear()).thenReturn(2015);

		assertEquals(BigDecimal.valueOf(27500), VehicleDepreciationCalculator.getCurrentVehicleValue(hondaCar));
		assertEquals(BigDecimal.valueOf(2500), VehicleDepreciationCalculator.getCurrentVehicleValue(hondaMotorcycle));

	}
}
JUnit result of running the testGetCurrentVehicleValue() method in TestVehicleDepreciationCalculatorclass.
Result of running the above test in Eclipse IDE.

As you may see above when we run this test it passes with the flying colors i.e. with no issues. So as you may have noticed that the method getCurrentVehicleValue(…) in our utility class VehicleDepreciationCalculator accepts objects from any classes that have implemented a Vehicle, Car or Motorcycle Interface.

A java interface provides a way to enforce a contract for the implementing classes to either provide an implementation for the methods exposed by the interface or declare themselves as abstract without any deviation, an exception to that are the marker interfaces.

Marker Interfaces

Marker interfaces mostly works like a tag on the class, that identify a special operation being handled by that class. These interfaces are mostly used to give a hint to the compiler that classes implementing them would have a special behavior and those should be handled carefully. These interfaces are empty interfaces without any function present in them.

Let’s take a look at one of the example marker interface that we could create for our continuing vehicle example.

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * A marker interface that if implemented marks a vehicle as a ZEV.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface ZeroEmissionVehicle {

}

Now we can use this marker interface to mark a vehicle as a zero emission vehicle. For example let’s create another class implementing the Car interface and mark it as a zero emission vehicle.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * Class representing cars built by Tesla.								*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public class TeslaCar extends GroundVehicle implements Car, ZeroEmissionVehicle {

	private CarStyle carStyle = null;

	/**
	 * @param make the make/brand name of the vehicle manufacturer.
	 * @param makeYear the make year of the vehicle.
	 * @param model Vehicle Model
	 * @param basePrice Vehicle base price.
	 * @param carStyle the style of the Car
	 */
	protected TeslaCar(String make, Integer makeYear, String model, BigDecimal basePrice, CarStyle carStyle) {
		super(make, makeYear, model, basePrice);
		this.carStyle = carStyle;
	}

	@Override
	public CarStyle getCarStyle() {
		return this.carStyle;
	}

	@Override
	public String getConfiguration() {
		return String.format("TeslaCar [carStyle=%s, make=%s, makeYear=%s, model=%s]", getCarStyle(), getMake(),
				getMakeYear(), getModel());
	}

}

Now we can utilize the fact that a car is a zero emission car to check it’s eligibility to drive in a car pool lane. For example let’s create a utility class to do that.

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * Utility class that checks whether a vehicle is eligible to be driven *
 * in a car pool lane.													*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public abstract class CarPoolLaneEligibilityChecker {

	/**
	 * Verifies the eligibility of a vehicle to be driven in a car pool lane.
	 * 
	 * @param vehicle vehicle to verify.
	 * @return whether it is eligible or not.
	 */
	public static boolean isEligible(Vehicle vehicle) {
		if (vehicle instanceof ZeroEmissionVehicle) {
			return true;
		}
		return false;
	}
}

Let’s check the behavior implemented by this utility class with a JUnit test.

package com.day2dayjava.tutorials.java.interfaces;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Test class for CarPoolLaneEligibilityChecker.						*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestCarPoolLaneEligibilityChecker {

	@Mock
	HondaCar hondaCar;
	@Mock
	TeslaCar teslaCar;

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.CarPoolLaneEligibilityChecker#isEligible(com.day2dayjava.tutorials.java.interfaces.Vehicle)}.
	 */
	@Test
	final void testIsEligible() {
		assertFalse(CarPoolLaneEligibilityChecker.isEligible(hondaCar));
		assertTrue(CarPoolLaneEligibilityChecker.isEligible(teslaCar));
	}

}

These both assertions passes as Honda car has not marked itself as a ZeroEmissionVehicle in our above example classes. Here is the result screen from eclipse after running this test.

JUnit result of running the testIsEligible() method in TestCarPoolLaneEligibilityChecker class.
Result of running the above test in Eclipse IDE.

Functional Interfaces

Java 8 introduced the new concept of Functional Interfaces. A functional interface is any interface that has only one abstract method i.e. a method that does not have an implementation. You can still have default and static methods with implementations inside that interface but the number of methods that are not implemented should not be more than one. Such interfaces in addition to the normal classes can also be implemented by the lambda expressions. You may refer to my post on Java Lambda Expressions to learn more about the lambda expressions.

For now let’s use the VehicleWash interface as our functional interface that we have described in the Interfaces and Java Generics section. This interface has a single abstract method wash(…). Here is a JUnit test case that implements this interface with a lambda expression and then use it’s wash method. If you find it hard to follow then you can skip this for now and come back to this section once you have gone through my post on lambda expressions.

package com.day2dayjava.tutorials.java.interfaces;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.day2dayjava.tutorials.java.interfaces.Car.CarStyle;

/**
 ************************************************************************
 *Tests for functional interface VehicleWash with a lambda expression.	*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestCarWashWithLambda {

	private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
	private final PrintStream originalOut = System.out;

	@Mock
	private HondaCar hondaCar;

	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outContent));
	}

	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOut);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.VehicleWash#wash(com.day2dayjava.tutorials.java.interfaces.Vehicle)}.
	 */
	@Test
	final void testVehicleWash() {
		when(hondaCar.getMake()).thenReturn("Honda");
		when(hondaCar.getMakeYear()).thenReturn(2020);
		when(hondaCar.getModel()).thenReturn("CRV EXL");
		when(hondaCar.getCarStyle()).thenReturn(CarStyle.SUV);
		when(hondaCar.getConfiguration()).thenCallRealMethod();

		VehicleWash<Vehicle> vehicleWashStation = (vehicle) -> {
			System.out.print(String.format("Your \"%s\" is clean now.", vehicle.getConfiguration()));
		};
		vehicleWashStation.wash(hondaCar);
		assertEquals("Your \"HondaCar [carStyle=SUV, make=Honda, makeYear=2020, model=CRV EXL]\" is clean now.",
				outContent.toString());
	}
}
Test result of implementing a functional interface with a lambda expression.
Test result of implementing a functional interface with a lambda expression.

Types of Concrete Methods

Since Java 8 you can also add a default method with body and static methods with body too, let’s take a look at an example of each.

Static Methods

As you might have noticed there was a lot of proliferation of utility classes in almost every application or APIs that a developer gets a chance to work on. Although you would never need to create an instance of such classes because all those utility methods were mostly created in an abstract class as a static method. Now you can do the same but using interfaces. Static methods follow the same rules in interface as they follow in java classes. Here is an example of an interface having a static method.

package com.day2dayjava.tutorials.java.interfaces;

import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 ************************************************************************
 * This interface provides a utility method to select the latest		*
 * vehicle out of the given vehicles.   								*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface LatestMakeVehicleSelector {

	static List<Vehicle> getLatestMakeVehicles(List<Vehicle> vehicles) {
		if (vehicles != null) {
			return vehicles.stream()
					.collect(Collectors.groupingBy(Vehicle::getMakeYear, TreeMap::new, Collectors.toList())).lastEntry()
					.getValue();
		}
		return Collections.emptyList();
	}
}

Let’s try to use the interface’s static method with a JUnit test.

package com.day2dayjava.tutorials.java.interfaces;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Test class for LatestMakeVehicleSelector.							*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestLatestMakeVehicleSelector {

	@Mock
	Vehicle vehicleOne, vehicleTwo, vehicleThree, vehicleFour, vehicleFive;

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.LatestMakeVehicleSelector#getLatestMakeVehicles(java.util.List)}.
	 */
	@Test
	final void testGetLatestMakeVehicles() {
		//Setup the Mock.
		when(vehicleOne.getMakeYear()).thenReturn(2020);
		when(vehicleTwo.getMakeYear()).thenReturn(2015);
		when(vehicleThree.getMakeYear()).thenReturn(2019);
		when(vehicleFour.getMakeYear()).thenReturn(2020);
		when(vehicleFive.getMakeYear()).thenReturn(2015);

		List<Vehicle> latestVehicles = LatestMakeVehicleSelector
				.getLatestMakeVehicles(Arrays.asList(vehicleOne, vehicleTwo, vehicleThree, vehicleFour, vehicleFive));

		assertEquals(vehicleOne, latestVehicles.get(0));
		assertEquals(vehicleFour, latestVehicles.get(1));
		assertEquals(vehicleOne.getMakeYear(), latestVehicles.get(0).getMakeYear());
		assertEquals(vehicleFour.getMakeYear(), latestVehicles.get(1).getMakeYear());
		assertEquals(latestVehicles.size(), 2);
	}
}

This test passes with all green colors.

JUnit result of running the testGetLatestMakeVehicles() method in TestLatestMakeVehicleSelector class.
Result of running the above test in Eclipse IDE.

Default Methods

Default methods are the methods that you declare and define with the “default” keyword in the method signature. All the implementing classes do not have to override it or declare themselves as abstract if they don’t want to implement a default method. They will simply inherit the implementation provided by the interface. Default methods allow the interface evolution.

You may either use them when

  1. You want to add or expose a new capability through your existing interface without breaking the interface’s contract with the implementing classes. In that case all the existing classes that are implementing this interface would just inherit the default behavior from the interface. But of-course they can override this method if they want to in order for them to provide an implementation that is more specific to them.
  2. You are writing a new interface and you want to provide an out of box implementation of those methods.

Note: Interface’s do not have a state, so default methods can not work with any state, they can only work with the method’s arguments. however you are free to use the this operator in a default method in that case this refers to the instance of the implementing class on which behalf this method was called.

Let’s continue on our Vehicle track and let’s say now we want to add a new feature that allows a car to have an economy mode of driving. In that case we could enhance our current Vehicle interface as follows.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * An interface to represent Vehicles. This interface has just methods. *
 * 																		*	
 * @author D2DJ 														*
 ************************************************************************
 **/
public interface Vehicle {
	//Returns the make/brand name of the vehicle manufacturer.
	String getMake();

	// Returns the make year of the vehicle.
	Integer getMakeYear();

	// Returns the Vehicle Model
	String getModel();

	//Returns the detailed specification of the Vehicle.
	String getConfiguration();

	//Returns the base price of the vehicle that goes out of the box 
	//without any customizations.
	BigDecimal getBasePrice();

	//Default method that provides each vehicle a capability to run in economy mode.
	//saving fuel and increasing mileage.
	default void turnOnEconomyMode() {
		throw new UnsupportedOperationException("Eco mode is not available for your vehicle.");
	}
}

And let’s override this default behavior which is just throwing an exception saying that “Eco mode is not available for your vehicle.”. Let’s override it in our HondaCar class but not in TeslaCar and see what we get when try to access the newly added feature method.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;

/**
 ************************************************************************
 * Class representing cars built by Honda.								*
 *																		*
 * @author D2DJ                                                      	*
 ************************************************************************
 **/
public class HondaCar extends GroundVehicle implements Car {

	private CarStyle carStyle = null;

	/**
	 * @param make the make/brand name of the vehicle manufacturer.
	 * @param makeYear the make year of the vehicle.
	 * @param model Vehicle Model
	 * @param basePrice Vehicle base price.
	 * @param carStyle the style of the Car
	 */
	protected HondaCar(String make, Integer makeYear, String model, BigDecimal basePrice, CarStyle carStyle) {
		super(make, makeYear, model, basePrice);
		this.carStyle = carStyle;
	}

	@Override
	public CarStyle getCarStyle() {
		return this.carStyle;
	}

	@Override
	public String getConfiguration() {
		return String.format("HondaCar [carStyle=%s, make=%s, makeYear=%s, model=%s]", getCarStyle(), getMake(),
				getMakeYear(), getModel());
	}

	@Override
	public void turnOnEconomyMode() {
		System.out.print("Economy mode is on now.");
	}
}

Now let’s check the behavior of accessing this method for Honda and Tesla car objects.

package com.day2dayjava.tutorials.java.interfaces;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.verify;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 ************************************************************************
 * Tests the default method feature provided by Java interfaces.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestDefaultMethods {

	private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
	private final PrintStream originalOut = System.out;

	@Mock
	private HondaCar hondaCar;

	@Mock
	private TeslaCar teslaCar;

	@BeforeEach
	public void setUpStreams() {
		System.setOut(new PrintStream(outContent));
	}

	@AfterEach
	public void restoreStreams() {
		System.setOut(originalOut);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.Vehicle#turnOnEconomyMode()}.
	 */
	@Test
	final void testDefaultMethods() {

		//Honda Car would run it's overridden method and would not throw the exception thrown by the default implementation.
		doCallRealMethod().when(hondaCar).turnOnEconomyMode();
		hondaCar.turnOnEconomyMode();
		verify(hondaCar).turnOnEconomyMode();
		assertEquals("Economy mode is on now.", outContent.toString());

		doCallRealMethod().when(teslaCar).turnOnEconomyMode();

		//Tesla Car would throw the exception thrown by the default implementation as it doesn't override the default behavior.
		UnsupportedOperationException unsupportedOperationException = assertThrows(UnsupportedOperationException.class,
				teslaCar::turnOnEconomyMode,
				"Tesla car is not supposed to have a economy mode and should have thrown UnsupportedOperationException.");
		assertEquals("Eco mode is not available for your vehicle.", unsupportedOperationException.getMessage());
	}
}

When we run this test we can see that it passes successfully. That asserts that TeslaCar object threw an UnsupportedOperationException because it exibited the default behavior that it inherited from the Vehicle interface. Whereas the HondaCar object exhibited the behavior that the HondaCar provided for this method.

JUnit result of running the testDefaultMethods() method in TestDefaultMethods class.
Result of running the above test in Eclipse IDE.

Private Methods

From Java 9 onward you can include private methods in Java interfaces. These private methods can only be accessed within the interface defining them and can not be inherited or used by the implementing classes or extending interfaces. You could add a static or non static private method. A static private method could be called by other static and non static member methods in the same interface. Whereas a non static private method could only be used by the other non static member methods. Here is an example of an interface that has a private static method.

package com.day2dayjava.tutorials.java.interfaces;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 ************************************************************************
 * Utility interface to keep the helper calculation methods for various *
 * vehicle matrices.													*
 * 																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface VehicleMetricsCalculationHelper {

	/**
	 * Returns the remaining distance Miles to which a vehicle could drive given the remaining fuel.
	 * 
	 * @param vehiclesMilesPerGallon vehicle average MPG
	 * @param remainingFuelInGallons remaining fuel in vehicle.
	 * @return how many miles the vehicle can go with the given remaining fuel.
	 */
	public static BigDecimal getAvailableDrivingDistanceInMiles(BigDecimal vehiclesMilesPerGallon,
			BigDecimal remainingFuelInGallons) {
		return getAvailableDrivingDistance(vehiclesMilesPerGallon, remainingFuelInGallons);
	}

	/**
	 * Returns the remaining distance in KiloMeters to which a vehicle could drive given the remaining fuel.
	 * 
	 * @param vehiclesKiloMetersPerLitre vehicle average KMPL
	 * @param remainingFuelInLitres remaining fuel in vehicle.
	 * @return how many Kilo Meters the vehicle can go with the given remaining fuel.
	 */
	public static BigDecimal getAvailableDrivingDistanceInKiloMeters(BigDecimal vehiclesKiloMetersPerLitre,
			BigDecimal remainingFuelInLitres) {
		return getAvailableDrivingDistance(vehiclesKiloMetersPerLitre, remainingFuelInLitres);
	}

	/**
	 * Returns the remaining distance to which a vehicle could drive given the remaining fuel.
	 * 
	 * @param vehicesFuelAverage vehicle average.
	 * @param remainingFuel remaining fuel in vehicle.
	 * @return how far the vehicle can go with the given remaining fuel.
	 */
	private static BigDecimal getAvailableDrivingDistance(BigDecimal vehicesFuelAverage, BigDecimal remainingFuel) {
		return remainingFuel.divide(vehicesFuelAverage, RoundingMode.DOWN);
	}
}

Let’s test these public interface methods to see that they are calling the private static method internally.

package com.day2dayjava.tutorials.java.interfaces;

import static com.day2dayjava.tutorials.java.interfaces.VehicleMetricsCalculationHelper.getAvailableDrivingDistanceInKiloMeters;
import static com.day2dayjava.tutorials.java.interfaces.VehicleMetricsCalculationHelper.getAvailableDrivingDistanceInMiles;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.math.BigDecimal;

import org.junit.jupiter.api.Test;

/**
 ************************************************************************
 * Test	class for VehicleMetricsCalculationHelper						*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
class TestVehicleMetricsCalculatorHelper {

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.VehicleMetricsCalculationHelper#getAvailableDrivingDistanceInMiles(java.math.BigDecimal,java.math.BigDecimal)}.
	 */
	@Test
	final void testGetAvailableDrivingDistanceInMiles() {
		BigDecimal vehiclesMilesPerGallon = BigDecimal.valueOf(25);
		BigDecimal remainingFuelInGallons = BigDecimal.valueOf(2.5);
		BigDecimal availableDrivingDistanceInMiles = getAvailableDrivingDistanceInMiles(vehiclesMilesPerGallon,
				remainingFuelInGallons);
		assertEquals(BigDecimal.valueOf(62.5), availableDrivingDistanceInMiles);
	}

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.VehicleMetricsCalculationHelper#getAvailableDrivingDistanceInKiloMeters(java.math.BigDecimal,java.math.BigDecimal)}.
	 */
	@Test
	final void testGetAvailableDrivingDistanceInKiloMeters() {
		BigDecimal vehiclesKiloMetersPerLitre = BigDecimal.valueOf(25);
		BigDecimal remainingFuelInLitres = BigDecimal.valueOf(2.5);
		BigDecimal availableDrivingDistanceInKiloMeters = getAvailableDrivingDistanceInKiloMeters(
				vehiclesKiloMetersPerLitre, remainingFuelInLitres);
		assertEquals(BigDecimal.valueOf(62.5), availableDrivingDistanceInKiloMeters);
	}
}

And we can see in the below results that both the tests passed.

JUnit result of running the tests present in TestVehicleMetricsCalculatorHelper class.
Result of running the above test in Eclipse IDE.

In a similar way you could add private default methods too. I am not adding an example of that here and would let you to give it a try and practice.

Interfaces and Java Generics

You can use Java Generics feature with interfaces the same way you could use it in classes. For example you can create a generic VehicleWash interface that represents a vehicle wash facility and exposes the basic wash operation.

Generic Interface

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * This interface represents a vehicle wash facility and exposes the    *
 * basic wash operation.												*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public interface VehicleWash<T extends Vehicle> {
	void wash(T vehicle);
}

And then can do a generic or a specific implementation of this interface. Here are the examples for each type

Generic Implementation

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * Generic implementation of VehicleWash that can wash any kind of		* 
 * Vehicles.															*
 * 																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public class GenericVehicleWash<T extends Vehicle> implements VehicleWash<T> {

	@Override
	public void wash(T vehicle) {
		System.out.println(String.format("Your \"%s\" is clean now.", vehicle.getConfiguration()));
	}
}

Let’s test this generic implementation with a JUnit test.

package com.day2dayjava.tutorials.java.interfaces;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.day2dayjava.tutorials.java.interfaces.Car.CarStyle;
import com.day2dayjava.tutorials.java.interfaces.Motorcycle.MotorCycleStyle;

/**
 ************************************************************************
 * Tests for GenericVehicleWash.										*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestGenericVehicleWash {

	@Mock
	private HondaCar hondaCar;
	@Mock
	private HondaMotorcycle hondaMotorcycle;

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.GenericVehicleWash#wash(com.day2dayjava.tutorials.java.interfaces.Vehicle)}.
	 */
	@Test
	final void testWash() {
		//Setup the mocks.
		when(hondaCar.getMake()).thenReturn("Honda");
		when(hondaCar.getMakeYear()).thenReturn(2020);
		when(hondaCar.getModel()).thenReturn("CRV EXL");
		when(hondaCar.getCarStyle()).thenReturn(CarStyle.SUV);
		when(hondaCar.getConfiguration()).thenCallRealMethod();

		when(hondaMotorcycle.getMake()).thenReturn("Honda");
		when(hondaMotorcycle.getMakeYear()).thenReturn(2020);
		when(hondaMotorcycle.getModel()).thenReturn("Pulsor");
		when(hondaMotorcycle.getMotorCycleStyle()).thenReturn(MotorCycleStyle.STANDARD);
		when(hondaMotorcycle.getConfiguration()).thenCallRealMethod();

		VehicleWash<HondaCar> vehicleCarWashStation = new GenericVehicleWash<>();
		vehicleCarWashStation.wash(hondaCar);
		verify(hondaCar).getConfiguration();

		VehicleWash<HondaMotorcycle> vehicleMotorcycleWashStation = new GenericVehicleWash<>();
		vehicleMotorcycleWashStation.wash(hondaMotorcycle);
		verify(hondaMotorcycle).getConfiguration();
	}
}

You can see that with the generic interface and generic implementation you could use the same implementation of wash for both a car and a motorcycle. And you can see the test execution results below that passed all our assertions validating that the proper class methods were called on executing the generic wash method.

JUnit result of running the testWash() method in TestGenericVehicleWash class.
Result of running the above test in Eclipse IDE.

And the following output at the console:

Your "HondaCar [carStyle=SUV, make=Honda, makeYear=2020, model=CRV EXL]" is clean now.
Your "HondaMotorcycle [motorCycleStyle=STANDARD, make=Honda, makeYear=2020, model=Pulsor]" is clean now.

Specific Implementation

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * Specific implementation of VehicleWash that washes only cars.		*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public class CarWash implements VehicleWash<Car> {

	@Override
	public void wash(Car car) {
		System.out.println(String.format("Your car \"%s\" is clean now.", car.getConfiguration()));
	}

}

Let’s write a small program to test this specific to Cars implementation of VehicleWash.

package com.day2dayjava.tutorials.java.interfaces;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.day2dayjava.tutorials.java.interfaces.Car.CarStyle;

/**
 ************************************************************************
 * Tests for class CarWash												*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestCarWash {

	@Mock
	private HondaCar hondaCar;

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.CarWash#wash(com.day2dayjava.tutorials.java.interfaces.Car)}.
	 */
	@Test
	final void testWash() {
		//Setup the mocks.
		when(hondaCar.getMake()).thenReturn("Honda");
		when(hondaCar.getMakeYear()).thenReturn(2020);
		when(hondaCar.getModel()).thenReturn("CRV EXL");
		when(hondaCar.getCarStyle()).thenReturn(CarStyle.SUV);
		when(hondaCar.getConfiguration()).thenCallRealMethod();

		VehicleWash<Car> carWashStation = new CarWash();
		carWashStation.wash(hondaCar);
		verify(hondaCar).getConfiguration();
	}
}

And here are the results of running this unit test for our CarWash implementation.

JUnit result of running the testWash() method in TestCarWash class.
Result of running the above test in Eclipse IDE.

And the following output at the console:

Your car "HondaCar [carStyle=SUV, make=Honda, makeYear=2020, model=CRV EXL]" is clean now.

Same way we could also implement this interface for the Motorcycle wash.

package com.day2dayjava.tutorials.java.interfaces;

/**
 ************************************************************************
 * Specific implementation of VehicleWash that washes only motorcycles.	*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
public class MotorcycleWash implements VehicleWash<Motorcycle> {

	@Override
	public void wash(Motorcycle motorcycle) {
		System.out.println(String.format("Your motorcycle \"%s\" is clean now.", motorcycle.getConfiguration()));
	}

}

And let’s write a small JUnit test for this class too.

package com.day2dayjava.tutorials.java.interfaces;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.day2dayjava.tutorials.java.interfaces.Motorcycle.MotorCycleStyle;

/**
 ************************************************************************
 * Tests for class MotorcycleWash										*
 *																		*
 * @author D2DJ															*
 ************************************************************************
 **/
@ExtendWith(MockitoExtension.class)
class TestMotorcycleWash {

	@Mock
	private HondaMotorcycle hondaMotorcycle;

	/**
	 * Test method for {@link com.day2dayjava.tutorials.java.interfaces.MotorcycleWash#wash(com.day2dayjava.tutorials.java.interfaces.Motorcycle)}.
	 */
	@Test
	final void testWash() {
		//Setup the mocks.
		when(hondaMotorcycle.getMake()).thenReturn("Honda");
		when(hondaMotorcycle.getMakeYear()).thenReturn(2020);
		when(hondaMotorcycle.getModel()).thenReturn("Pulsor");
		when(hondaMotorcycle.getMotorCycleStyle()).thenReturn(MotorCycleStyle.STANDARD);
		when(hondaMotorcycle.getConfiguration()).thenCallRealMethod();

		VehicleWash<Motorcycle> motorcycleWashStation = new MotorcycleWash();
		motorcycleWashStation.wash(hondaMotorcycle);
		verify(hondaMotorcycle).getConfiguration();
	}
}

And here are the results of running this unit test for our MotorcycleWash implementation.

JUnit result of running the testWash() method in TestMotorcycleWash class.
Result of running the above test in Eclipse IDE.

And the following output at the console:

Your motorcycle "HondaMotorcycle [motorCycleStyle=STANDARD, make=Honda, makeYear=2020, model=Pulsor]" is clean now.

Abstract Classes Vs Interfaces

There are a lot of things that are common between abstract classes and interfaces and sometimes you will find yourself wondering why both things still exist in Java. Mostly you should choose what to use based on some basic criteria or conventions. For example

  • If the only thing you are going to have in your class are the static methods and never going to create an instance of it then better use an interface.
  • A class can implement more than one interface and inherit their default methods and still can extend another class if needed. So you can use multiple inheritance using interfaces but not abstract classes.
  • Abstract classes are mostly used as a parent class to abstract out the common code related to the child classes for reuse, provide default implementations of some methods or control the accessibility of certain operations for the extending classes.
  • Methods declared in an interface are always public in the implementing classes.

Source Download

You can download full eclipse project from my Github repository using the following link: https://github.com/Day2DayJava/java

What’s Next

Next we will learn about the Java lambda Expressions.

Related Posts

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: