Archive

Posts Tagged ‘java 8’
  • Lambdas and Clean Code

    Buddhist monk washing clothes in the river

    As software developers, we behave like children. When we see shiny new things, we just have to play with them. That’s normal, accepted, and in general, even beneficial to our job… up to a point.

    When Java started to provide annotations with version 5, there was a huge move toward using them. Anywhere. Everywhere. Even when it was not a good idea to. But it was new, hence it had to be good. Of course, when something is abused, there’s a strong movement against it. So that even when the usage of annotations may make sense, some developers might strongly be against it. There’s even a site about that (warning, trolling inside).

    Unfortunately, we didn’t collectively learn from overusing annotations. With a lot of companies having migrated to Java 8, one start to notice a lot of code making use of lambdas like this one:

    List<Person> persons = ...;
    persons.stream().filter(p -> {
        if (p.getGender() == Gender.MALE) {
            return true;
        }
        LocalDate now = LocalDate.now();
        Duration age = Duration.between(p.getBirthDate(), now);
        Duration adult = Duration.of(18, ChronoUnit.YEARS);
        if (age.compareTo(adult) > 0) {
            return true;
        }
        return false;
    }).map(p -> p.getFirstName() + " " + p.getLastName())
      .collect(Collectors.toList());
    

    This is just a stupid sample, but it gives a good feeling of the code I sometimes have to read. It’s in general longer and even more convoluted, or to be politically correct, it has room for improvement - really a lot of room.

    The first move would be to apply correct naming, as well as move the logic to where it belongs to.

    public class Person {
    
        // ...
    
        public boolean isMale() {
            return getGender() == Gender.MALE;
        }
    
        public boolean isAdult(LocalDate when) {
            Duration age = Duration.between(birthDate, when);
            Duration adult = Duration.of(18, ChronoUnit.YEARS);
            return age.compareTo(adult) > 0;
        }
    }
    

    This small refactoring already improves the readability of the lambda:

    persons.stream().filter(p -> {
        if (p.isMale()) {
            return true;
        }
        LocalDate now = LocalDate.now();
        if (p.isAdult(now)) {
            return true;
        }
        return false;
    }).map(p -> p.getFirstName() + " " + p.getLastName())
            .collect(Collectors.toList());
    

    But it shouldn’t stop there. There’s an interesting bia regarding lambda: they have to be anonymous. Nearly all examples on the Web show anonymous lambdas. But nothing could be further from the truth!

    Let’s name our lambdas, and check the results:

    // Implementation details
    Predicate<Person> isMaleOrAdult = p -> {
        if (p.isMale()) {
            return true;
        }
        LocalDate now = LocalDate.now();
        if (p.isAdult(now)) {
            return true;
        }
        return false;
    };
    Function<Person, String> concatenateFirstAndLastName = p -> p.getFirstName() + " " + p.getLastName();
    
    // Core
    persons.stream().filter(isMaleOrAdult).map(concatenateFirstAndLastName)
    

    Nothing mind-blowing. Yet, notice that the stream itself (the last line) has become more readable, not hidden behind implementation details. It doesn’t prevent developers from reading them, but only if necessary.

    Conclusion

    Tools are tools. Lambdas are one of them, in a Java developer’s toolsbelt. But concepts are forever.

    Categories: Java Tags: lambdaclean codejava 8
  • Custom collectors in Java 8

    Among the many features available in Java 8, streams seem to be one of the biggest game changers regarding the way to write Java code. Usage is quite straightforward: the stream is created from a collection (or from a static method of an utility class), it’s processed using one or many of the available stream methods, and the collected back into a collection or an object. One generally uses one of the static method that the Collectors utility class offers:

    • Collectors.toList()
    • Collectors.toSet()
    • Collectors.toMap()
    • etc.

    Sometimes, however, there’s a need for more. The goal of this post is to describe how to achieve that.

    The Collector interface

    Every one of the above static methods returns a Collector. But what is a Collector? The following is a simplified diagram:

    Interface From the JavaDocs
    Supplier Represents a supplier of results. There is no requirement that a new or distinct result be returned each time the supplier is invoked.
    BiConsumer Represents an operation that accepts two input arguments and returns no result. Unlike most other functional interfaces, BiConsumer is expected to operate via side-effects.
    Function Represents a function that accepts one argument and produces a result.
    BinaryOperator Represents an operation upon two operands of the same type, producing a result of the same type as the operands. This is a specialization of BiFunction for the case where the operands and the result are all of the same type.

    The documentation of each dependent interface doesn’t tell much, apart from the obvious. Looking at the Collector documentation yields a little more:

    A Collector is specified by four functions that work together to accumulate entries into a mutable result container, and optionally perform a final transform on the result. They are:

    • creation of a new result container (supplier())
    • incorporating a new data element into a result container (accumulator())
    • combining two result containers into one (combiner())
    • performing an optional final transform on the container (finisher())

    The Stream.collect() method

    The real insight comes from the Stream.collect() method documentation:

    Performs a mutable reduction operation on the elements of this stream. A mutable reduction is one in which the reduced value is a mutable result container, such as an ArrayList, and elements are incorporated by updating the state of the result rather than by replacing the result. This produces a result equivalent to:

        R result = supplier.get();
        for (T element : this stream)
            accumulator.accept(result, element);
        return result;
    

    Note the combiner() method is not used - it is only used within parallel streams, and for simplification purpose, will be set aside for the rest of this post.

    Examples

    Let’s have some examples to demo the development of custom collectors.

    Single-value example

    To start, let’s compute the size of a collection using a collector. Though not very useful, it’s a good introduction. Here are the requirements for the 4 interfaces:

    1. Since the end result should be an integer, the supplier should probably also return some kind of integer. The problem is that neither int nor Integer are mutable, and this is required for the next step. A good candidate type would be MutableInt from Apache Commons Lang.
    2. The accumulator should only increment the MutableInt, whatever the element in the collection is.
    3. Finally, the finisher just returns the int value wrapped by the MutableInt.

    Source is available on Github.

    Grouping example

    The second example shall be more useful. From a collection of strings, let’s create a Apache Commons Lang multi-valued map:

    • The key should be a char
    • The corresponding values should be the strings that start with this char
    1. The supplier is pretty straightforward, it returns a MultiValuedMap instance
    2. The accumulator just calls the put method from the multi-valued map, using the above “specs”
    3. The finisher returns the map itself

    Source is available on Github.

    Partitioning example

    The third example matches a use-case I encountered this week: given a collection and a predicate, dispatch elements that match into a collection and elements that do not into another.

    1. As the supplier returns a single instance, a new data structure e.g. DoubleList should first be designed
    2. The accumulator must be initialized with the predicate, so that the accept() contract method signature is the same.
    3. As for the above example, the finisher should return the DoubleList itself

    Source is available on Github.

    Final consideration

    Developing a custom collector is not that hard, provided one understands the basic concepts behind it.

    The real issue behind collectors is the whole Stream API. Streams need to be created first and then collected afterwards. Newer languages, with Functional Programming paradigm designed from the start - such as Scala or Kotlin, provide collections with such capabilities directly backed-in.

    For example, to filter out something from a map in Java:

    map.entrySet().stream()
            .filter( entry -> entry.getKey().length() == 4)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    

    That would translate as the following in Kotlin:

    map.entries.filter { it.key.length == 4 }
    
    Categories: Java Tags: java 8lambdacollector
  • Optional dependencies in Spring

    I’m a regular Spring framework user and I think I know the framework pretty well, but it seems I’m always stumbling upon something useful I didn’t know about. At Devoxx, I learned that you could express conditional dependencies using Java 8’s new Optional&lt;T&gt; type. Note that before Java 8, optional dependencies could be auto-wired using @Autowired(required = false), but then you had to check for null.

    How good is that? Well, I can think about a million use-cases, but here are some that come out of my mind:

    • Prevent usage of infrastructure dependencies, depending on the context. For example, in a development environment, one wouldn’t need to send metrics to a MetricRegistry
    • Provide defaults when required infrastructure dependencies are not provided e.g. a h2 datasource
    • The same could be done in a testing environment.
    • etc.

    The implementation is very straightforward:

    @ContextConfiguration(classes = OptionalConfiguration.class)
    public class DependencyPresentTest extends AbstractTestNGSpringContextTests {
    
        @Autowired
        private Optional<HelloService> myServiceOptional;
    
        @Test
        public void should_return_hello() {
            String sayHello = null;
            if (myServiceOptional.isPresent()) {
                sayHello = myServiceOptional.get().sayHello();
            }
    
            assertNotNull(sayHello);
            assertEquals(sayHello, "Hello!");
        }
    }
    

    At this point, not only does the code compile fine, but the dependency is evaluated at compile time. Either the OptionalConfiguration contains the HelloService bean - and the above test succeeds, or it doesn’t - and the test fails.

    This pattern is very elegant and I suggest you list it into your bag of available tools.

    Categories: Java Tags: java 8spring