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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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.