JAVA SE 8 Best practices

First of all this post is as per my view on what I experienced and what I learned through various forums. I do admit that I could be wrong with respect to others perspective, but that is another debate. However am always open for healthy discussion so do let me know by dropping a comment for this post.

Below are some best practices for JAVA SE 8:-

    • Lambdas:- 1) It has been recognised by various JAVA experts that earlier versions before java 8u40 have annoying lambda, java compiler issues. So it’s best to use java 8u40 or later versions.
      2) Prefer lambdas wherever applicable, avoid anonymous inner class use, as it just creates noise.

      //Java 7
      List<Person> people=loadPeople();
      Collections.sort(people, new Comparator<Person>(){
         @override
         public int compare(Person p1, Person p2){
            return p1.name.compareTo(p2.name);
         }
      });
      
      //Java 8 less noise
      List<Person> people=loadPeople();
      Collections.sort(people, (Person p1, Person p2)->p1.name.compareTo(p2.name));
      
      //Let's remove some more noise by letting target typing doing it's work
      List<Person> people=loadPeople();
      Collections.sort(people, (p1, p2)->p1.name.compareTo(p2.name));
      

      3) Do not use parameter brackets when optional.

      //prefer
      str->str.toUpperCase(Locale.US);
      
      //avoid
      (str)->str.toUpperCase(Locale.US);
      

      4) Do not declare local variables as ‘final’, instead use ‘effectively final’ concept. With ‘effectively final’ compiler expects a variable as final (if it needs to be).
      5) Prefer expression lambdas over block lambdas.

      //prefer
      str->str.toUpperCase(Locale.US);
      
      //use with care
      str->{
            return str.toUpperCase(Locale.US);
           }
      

      Use a separate method, if necessary. If you need to write more code inside your lambda, put it into a method and make your lambda call that method. By this, you will still have an expression based lambda.
      6) Lambdas for abstraction. 2 large methods contain same code except for one bit in the middle. We can use lambda to express the difference.

      private int doFoo(){
      //lots of code
      //logic specific to method1
      //lots of code
      }
      
      private int doBar(){
      //lots of code
      //logic specific to method2
      //lots of code
      }
      
      
      //let's write above in a better way
      private int doFoo(){
        return doFooBar(lambdaOfSpecificLogic);
      }
      
      private doFooBar(Function<A,B> fn){
      //lots of code
      result=fn.apply(arg);
      //lots of code
      }
      
    • Functional Interfaces:- 1) An interface with a single abstract method like Runnable, Comparable, Callable. Java SE 8 adds many new functional interfaces, see java.util.function package.
      2) Only write your own if extra semantics are valuable. If writing one, use @FunctionalInterface.

      //protecting against future maintenance of code by adding another abstract method 
      @FunctionalInterface
      public interface FooBarQuery{
        public abstract Foo findAllFoos(Bar bar);
      }
      

      3) Lambdas uses target typing, so avoid method overloads. As compiler will treat them as ambiguous methods.

      //avoid
      public class Foo<T>{
        public Foo<R> apply(Function<T,R> fn){
        //lots of code
        }
        public Foo<T> apply(UnaryOperator<T> fn){
        //lots of code
        }
      }
      

      Highly recommended to use different method names to avoid clashes.

      //prefer
      public class Foo<T>{
        public Foo<R> applyFunction(Function<T,R> fn){
        //lots of code
        }
        public Foo<T> applyOperator(UnaryOperator<T> fn){
        //lots of code
        }
      }
      

      4) When method takes mixture of Functional Interface and non-Functional Interface as arguments, prefer to have Functional Interface as last argument. It’s mostly stylistic, better IDE error recovery.

      //prefer
      public Foo parse(Locale locale, Function<Locale, Foo> fn);
      
      //avoid
      public Foo parse(Function<Locale, Foo> fn, Locale locale);
      
    • Exceptions:- 1) Lambdas only deal with Runtime exceptions, most functional interfaces do not declare exceptions. There’s no simple way to put checked exceptions in lambdas.
      //does not compile
      public Function<String, Class> loader(){
        return className->Class.forName(className); //throws a checked exception
      }
      

      2) Write or find a helper method, see ‘Unchecked’ from OpenGamma strata. This library converts checked exception to unchecked.

      //does not compile
      public Function<String, Class> loader(){
        return Unchecked.function(className->Class.forName(className)); 
      }
      

      3) This library can also convert any checked exception to runtime exception.

      //does not compile
      public Function<String, Class> loader(){
        return Unchecked.wrap(className->Class.forName(className)); 
      }
      

      4) Testing for exceptions often needs to test for exceptions using unit testing.

      public void testConstructorRejecsEmptyString(){
        try{
          new FooBar("");
          fail();
        }catch(IllegalArgumentException ex){
          //expected
        }
      }
      

      Use a helper method. See ‘TestHelper’ from OpenGamma Strata. Lots of variations on these are possible.

      public void testConstructorRejecsEmptyString(){
        TestHelper.assertThrows(IllegalargumentException.class, ()->new Foobar(""));
      }
      
    • Optional & null:- 1) New class ‘Optional’ added to JAVA 8. Simple concept- two states. Present, with a value ‘Optional.of(foo)’, empty ‘Optinal.empty()’.
      Standard code using null.

      //library returns null if not found
      public Foo findFoo(String key){----}
      
      //application code must remember to check for null
      Foo foo=findFoo(key);
      if(foo==null){
        foo=Foo.DEFAULT;//or throw an exception
      }
      

      2) Standard code using Optional.

      //library returns Optional if not found
      public Optional<Foo> findFoo(String key){----}
      
      //application code 
      Foo foo=findFoo(key).orElse(Foo.DEFAULT);
      //or
      Foo foo=findFoo(key).orElseThrow(RunTimeException::new);
      

      3) Variable of type Optional must never be null.
      4) Prefer “functional” methods like “orElse”.
      5) Using “isPresent()” a lot is misusing the feature.

      //prefer
      Foo foo=findFoo(key).orElse(Foo.DEFAULT);
      
      //avoid ifPresent()
      Optional<Foo> optFoo=findFoo(key);
      if(optFoo.isPresent()){-----}
      

      6) Use Optional instead of “null” on public return types.

    • Streams:- 1) Streams are new APIs for processing with lambdas. Most loops are the same, they have repetitive design patterns. Stream library provides an abstraction for these repetitive design patterns. Lambdas are used to pass the interesting bits, these lamdbas act on each element on the Stream.
      List<Trade> trades=loadTrades(); //output is stored here
      List<Money> valued=new ArrayList<Money>(); //loop to build output
      for(Trade t: trades){ //only interested in some trades
        if(t.isActive()){
          Money pv=presentValue(t); //converts each trade into the money value
          valued.add(pv);
        }
      }
      

      Most things are noise here. Important bits are List, trades, t.isActive(), presentvalue(t).
      Let’s focus on main things.

      List<Trade> trades=loadTrades(); //output is stored here
      List<Money> valued=trades.stream()
                         .filter(t->t.isActive())
                         .map(t->presentValue(t))
                         .collect(Collectors.toList());
      

      2) Streams are great sometimes, design focus was on Collections not Maps.
      3) Key goal was simple parallelism.
      4) Don’t overdo Streams. Streams not always more readable than loop, it’s painful debugging.
      5) Streams are good for collections, less so for Maps.
      6) Streams over ‘Map’ best with a dedicated wrapper. See ‘MapStream’ from OpenGamma Strata.
      7) Don’t obsess about method references.
      8) Learn to love ‘Collector’ interface.

    • Interfaces:- 1) Interfaces now have super powers, like default methods, it’s a normal method but on an interface., from now on interfaces have static methods. Why default methods, so that backward compatibility is maintained.
      2) Instead of factory class, use static method on interface.
      3) Instead of abstract class, use interface with defaults.
      4) Use modifiers in interfaces, much clearer picture now, as there are different types of methods.

      public interface Foo{
        public static of(String key){
        -------
        }
      
        public abstract getKey();
        
        public default isActive(){
        ------
        }
      }
      
    • Date and Time:- 1) new Date and Time APIs in JAVA 8, it covers dates, times, instants, periods, durations.
      2) This new API brings 80% plus of Josa-Time to the JDK.
      3) Fixes the mistakes in Joda-time.
      4) Move away from Joda-Time.
      5) Avoid java.util.Date and java.util.Calendar
      6) Focus on four most useful types:- LocalDate, LocalTime, ZonedDateTime, Instant
      7) Temporal interfaces are low-level, use concrete types.

      //prefer
      LocalDate date=LocalDate.of(2015,10,15);
      
      //avoid
      Temporal date =LocalDate.of(2015,10,15);
      

Leave a Reply

Your email address will not be published. Required fields are marked *