applying java 8 idioms to existing code – trisha gee – qcon

For more QCon posts, see my live blog table of contents. Last year, she talked about creating new apps in Java 8. This year is about migrating to Java 8 for “legacy” code. She showed specific JetBrains IDEA features. I just took notes on the concepts that are IDE agnostic. Presentation will be at bit.ly/refJ8

Usage from audience poll

  • About half compile with Java 7
  • About half compile with Java 8
  • About a quarter (half of Java 8 people)  compile with Java 8, but apps still looks like Java 7

Why use Java 8

  • Performance improvements in common data structure
  • Fork/Join speed improvements
  • Changes to support concurrency
  • Fewer lines of code – less boilerplate
  • New solutions to problems – more elegant/easier to write code
  • Minimize errors – ex: Optional avoids NullPointerException

Before refactoring

  • Make sure have high test coverage
  • Focus on method level changes – tests should still work
  • Have performance tests too to ensure not negatively impacting system
  • Decide on goals – performance? clearer code? easier to write new code? easier to read new code? use lambdas because team used to it? developer morale?
  • Limit the scope – small incremental commits. Don’t make change that will affect whole code base.

Straightforward refactoring of lambda expressions

  • Predicate, Comparator and Runnable are common places where might have used anonymous inner classes and can switch to lambdas
  • IDEs suggest where can replace code with lambdas (SonarLint does as well)
  • Quick win – reduce old boilerplate
  • [She didn’t talk about this, but it will make your code coverage go down. Make sure your management doesn’t worry about that!]
  • Lose explicit type information when use lambda. Might want to extra to method to make it clearer and call the method from the lambda.

Abstract classes – candidates for lambdas

  • See if can use an interface (if single abstract method) so can start using lambdas
  • Tag with @FunctionalInterface to make clearer
  • Then look at implementers (especially anonymous inner classes) and see if can convert to lambda expressions

Conditional logging

  • if (debug) { log.debug() } pattern is a good candidate for logging
  • Often people forget to add that conditional so opportunity to defer evaluation for those too via search/replace. Also, protects against conditional not matching log level. if (debug)  { log.severe() }. Uh oh!
  • Use a default method on the existing logging interface that takes a supplier instead of a String and has the if check

Collections and Streams API

  • Turn for loops into collect() or forEach()
  • Remember you can call list.forEach() directly without going through a stream
  • If do something like get rid of initial list size, test performance before and after to make sure not a needed optimization
  • The automatic streams refactoring might not result in shorter code. Think about whether the surrounding code wants to be refactored as well. A more dramatic refactoring. Go back to later. Also, avoid refactoring if and else code since not  a simple filter.
  • Remember to include method references when converting code too
  • Watch for surrounding code. For example, if sorting a list, move into the the stream. Or if iterating over new list, add as a forEach instead of turning into a list.

Optional

  • Be careful. Refactor locally.
  • Easy to accidentally change something that requires changing a ton of code.

Performance

  • Lambdas expressions don’t necessarily perform better than anonymous inner classes. Similarly for streams.
  • Using lambdas to add conditional logging is a big quick win performance improvement.
  • Streams didn’t need the “initial size of list” optimization
  • Adding parallel() to stream operations sometimes performs better and sometimes worse. Simple operation often performs worse in parallel because of overhead of parallelizing.
  • Introducing Optionals increases cost.

Java 7 refactorings

  • Use static imports
  • Reudndant type in diamond operator for generics

Leave a Reply

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