Categories
Java

Functional style programming with Optional in Java 8

With the Java 8, we have a more functional style programming available and the Optional type. This type is like Option in Scala and should replace the commonly used pattern of returning null if something is not found or available.

See the below example of a repository – authenticateUser2 returns a null if it cannot authenticate the user.

Using Optional, authenticateUser returns Optional.empty is it cannot authenticate the user, otherwise it will return an Optional that wraps the AuthenticationToken with Optional.of.


/**
* Repository for authentication.
* @author Koert Zeilstra
*/
@ApplicationScoped
public class AuthenticationRepository {
private Map<String, AuthenticationToken> tokenCache = new HashMap<>();
public Optional<AuthenticationToken> authenticateUser(String userName, String password) {
Optional<AuthenticationToken> result = Optional.empty();
if ("test".equals(userName) && "welcome".equals(password)) {
AuthenticationToken token = createAuthenticationToken(userName);
result = Optional.of(token);
}
return result;
}
public AuthenticationToken authenticateUser2(String userName, String password) {
AuthenticationToken result = null;
if ("test".equals(userName) && "welcome".equals(password)) {
result = createAuthenticationToken(userName);
}
return result;
}
/**
* @param userName User name.
* @return New authentication token.
*/
public AuthenticationToken createAuthenticationToken(String userName) {
UUID uuid = UUID.randomUUID();
LocalDateTime date = LocalDateTime.now().plusHours(8);
Set<String> roles = new HashSet<>();
User user = new User(userName, "Test", roles);
AuthenticationToken token = new AuthenticationToken(uuid.toString(), date, user);
tokenCache.put(token.getId(), token);
return token;
}
}

Using the classical null-checking pattern, LoginService2 uses an if-then-else structure:


/**
* Service to authenticate user with classical if-null checking.
* @author Koert Zeilstra
*/
@Path("/authentication")
@ManagedBean
public class LoginService2 {
@Inject
private AuthenticationRepository authenticationRepository;
@Path("/login")
@POST
@Consumes("application/x-www-form-urlencoded")
public Response login(@FormParam("username") String userName, @FormParam("password") String password) {
Response response = null;
AuthenticationToken authentication = authenticationRepository.authenticateUser2(userName, password);
if (authentication == null) {
response = Response.status(Response.Status.NOT_ACCEPTABLE).build();
} else {
AuthenticationToken authenticationToken = authenticationRepository.createAuthenticationToken(userName);
response = Response.ok(createResource(authenticationToken)).build();
}
return response;
}
private AuthTokenResource createResource(AuthenticationToken token) {
AuthTokenResource resource = new AuthTokenResource();
resource.token = token.getId();
resource.expiration = token.getExpiration();
return resource;
}
@XmlRootElement(name = "site")
public static class AuthTokenResource {
public LocalDateTime expiration;
public String token;
}
}

Using a functional style of checking for an empty result, LoginService uses the map and orElse methods:


/**
* Service to authenticate user with functional style.
* @author Koert Zeilstra
*/
@Path("/authentication")
@ManagedBean
public class LoginService {
@Inject
private AuthenticationRepository authenticationRepository;
@Path("/login")
@POST
@Consumes("application/x-www-form-urlencoded")
public Response login(@FormParam("username") String userName, @FormParam("password") String password) {
Optional<AuthenticationToken> authentication = authenticationRepository.authenticateUser(userName, password);
Response response = authentication.map(token -> Response.ok(createResource(token)).build())
.orElse(Response.status(Response.Status.NOT_ACCEPTABLE).build());
return response;
}
private AuthTokenResource createResource(AuthenticationToken token) {
AuthTokenResource resource = new AuthTokenResource();
resource.token = token.getId();
resource.expiration = token.getExpiration();
return resource;
}
@XmlRootElement(name = "site")
public static class AuthTokenResource {
public LocalDateTime expiration;
public String token;
}
}

This new style replaces 8 lines of code with 3 lines. You may wonder – what is the big deal – 5 lines of code?

At first glance, this map orElse pattern looks a little puzzling. After seeing this a few times, this new functional style describes the intention of the code more clearly than the if-then-else structure. The intention is that it returns a token, or else a not-acceptable status code.

The purpose of code is, next to implementing a solution, to communicate to the maintainer what you meant to do. We software developers sometimes spend too much time trying to understand what the code does, it would be great if we can see in one glance the intention – this will prevent many bugs and misunderstandings, and improve the quality of our work.