Writing beautiful code
with Java 8
sergiu.indrie@iquestgroup.com
Disclaimer
This is not a clean code presentation, but rather a code esthetics oriented
presentation which may include clean code.
Beautiful code?
● Easy to read/understand/write
● Concise
● DSL-like
● Clean code ++
Ugly vs Beautiful
for (int i = 0; i < meetings.size(); i++) {
System.out.println(meetings);
}
for (Meeting meeting : meetings) {
System.out.println(meeting);
}
meetings.forEach(System.out::println);
Ugly vs Beautiful
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Complex stuff");
}
}).start();
new Thread(() -> System.out.println("Complex stuff")).start();
Ugly vs Beautiful
Map<String, List<Meeting>> meetingsById = meetings.stream()
.collect(Collectors.groupingBy(Meeting::getId));
Map<String, List<Meeting>> meetingsGrouped = new HashMap<>();
for (Meeting meeting : meetings) {
if (!meetingsGrouped.containsKey(meeting.getId())) {
meetingsGrouped.put(meeting.getId(), new ArrayList<>());
}
meetingsGrouped.get(meeting.getId()).add(meeting);
}
Ugly vs Beautiful
// guarded logging
if (logger.isDebugEnabled()) {
logger.debug("This {} and {} with {} ", 1, that, compute());
}
VS
logger.debug("This {} ", () -> compute());
What’s “new” in Java 8?
● Lambdas
Runnable r2 = () -> System.out.println("Hello world two!");
● Streams
List<Room> rooms = microsoftExchangeService.getRoomLists().getItems().parallelStream()
.filter(this::isValidRoomList)
.map(this::retrieveRoomsInRoomList)
.flatMap(List::stream)
.collect(Collectors.toList());
● Optional
Optional<Meeting> meeting = meetingsDao.findById(meetingId);
meeting.ifPresent(this::setMeetingAsManuallyEnded);
PS - Help from IDEA
● Migration suggestions (more to come in IDEA 2016.3)
Enemy #1: Checked Exceptions
private static void checkedException() {
List<String> strings = Arrays.asList(1, 2, 3, 4, 5).stream()
.map(Exceptions::intToString)
.collect(Collectors.toList());
System.out.println(strings);
}
private static String intToString(Integer number) throws Exception {
if (number == 3) {
throw new Exception("wrong number, pal!");
}
return String.valueOf(number);
}
Enemy #1: Checked Exceptions
● Complex issue (see Brian Goetz’s post from 2010)
○ generic type parameters are monadic ⇒ one exact type
○ throws clauses are variadic ⇒ 0 or more types
● Solution?
Enemy #1: Checked Exceptions - Solution
● 1st Solution - Unchecked Exceptions*
● 2nd Solution - Wrap to 1st (see org.jooq.lambda.Unchecked)
public static <T> T unchecked(Callable<T> callable) {
try {
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
List<Room> rooms = roomAddresses.parallelStream()
.map(room -> unchecked(() -> getRoomWithoutMeetings(room)))
.collect(Collectors.toList());
* Python, Scala, C#, Ruby, PHP … don’t have checked exceptions
Enemy #1: Checked Exceptions - Solution
public static <T> T unchecked(Callable<T> callable) {
try {
return callable.call();
} catch (ApiServiceException e) {
throw new ApiServiceRuntimeException(e);
} catch (Exception e) {
throw runtime(e);
}
}
private static RuntimeException runtime(Throwable e) {
if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException(e);
}
Enemy #1: Checked Exceptions - Solution
● A more functional approach
Enemy #1: Checked Exceptions - Solution
● A more functional approach
String complexResult = Try.of(SomeClass::dangerousGet)
.recover(x -> Match(x).of(
Case(instanceOf(IllegalStateException.class), () -> "1st exception"),
Case(instanceOf(IllegalArgumentException.class), () -> "2nd exception")
))
.getOrElse("default2");
By the way
Java 9 brings: Collection Factory Methods* (all immutable)
+ some stream improvements like iterate, take/dropWhile
* Nevermind if you’ve been using Guava, jOOQ
Java 8 is nice, but don’t
// long lambdas
numbers.forEach(e -> {
int count = 0;
for(int i = 1; i <= e; i++) {
if(e % i == 0) count++;
}
System.out.println(count);
});
// unformatted streams
List<String> strings = Arrays.asList(1, 2, 3).stream().map(Object::toString)
.map(String::toUpperCase).limit(5).collect(Collectors.toList());
Java 8 is nice, but
Java 8 Computation Style
Level up: Javaslang
// Java 8
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
List<Integer> evenNumbers = integers.stream()
.filter(nr -> nr % 2 == 0)
.collect(Collectors.toList());
// Javaslang
List<Integer> integers = List.of(1, 2, 3, 4);
List<Integer> evenIntegers = integers.filter(nr -> nr % 2 == 0);
* javaslang.collection.List
Level up: Javaslang
● “...greatly inspired by Scala”
● Hence very functional
● facilitates functional programming through immutability
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
● List is really javaslang.collection.List :) but we do have toJavaList/Array/Collection/Set() etc.
public interface List<T> extends Kind1<List<?>, T>, LinearSeq<T>, Stack<T> {
default java.util.List<T> toJavaList() {
return ValueModule.toJavaCollection(this, new ArrayList<>());
}
● All Javaslang collections are Iterable and thus can be used in enhanced for-statements
for (String s : List.of("Java", "Advent")) {
// side effects and mutation
}
Level up: Javaslang
● Functional exception handling
// no need to handle exceptions
Try.of(SomeClass::bunchOfWork).getOrElse("default");
String complexResult = Try.of(SomeClass::dangerousGet)
.recover(x -> Match(x).of(
Case(instanceOf(IllegalStateException.class), () -> "1st exception"),
Case(instanceOf(IllegalArgumentException.class), () -> "2nd exception")
))
.getOrElse("default2");
Level up: Javaslang
● Lazy
Lazy<Double> lazy = Lazy.of(Math::random);
lazy.isEvaluated(); // = false
lazy.get(); // = 0.123 (random generated)
lazy.isEvaluated(); // = true
lazy.get(); // = 0.123 (memoized)
● + other FP features like function composition, currying, memoization, lifting,
immutable collections, tuples
Level up: Javaslang
Or maybe just switch to Scala :D
// type inference, nice constructors, native streams API, no semicolons :)
val integers = List(1, 2, 3, 4)
val evenIntegers = integers.filter(_ % 2 == 0)
// pre/post/infix operators
val sum = (1 to 10).sum
// immutable, generated equals/getter/toString/hashcode, pattern matching decomposition
case class Person(firstName: String, lastName: String)
object Singleton {}
// immutable collections, XML processing, multiple inheritance, tuples, REPL etc.
Or maybe just switch to Scala :D
// pattern matching & decomposition
object Demo {
def main(args: Array[String]) {
val alice = new Person("Alice", 25)
val charlie = new Person("Charlie", 32)
for (person <- List(alice, charlie)) {
person match {
case Person("Alice", 25) => println("Hi Alice!")
case Person(name, age) => println(
"Age: " + age + " year, name: " + name + "?")
}
}
}
case class Person(name: String, age: Int)
}
References
https://github.com/tedyoung/awesome-java8
https://blog.jooq.org/2014/05/02/java-8-friday-lets-deprecate-those-legacy-libs/
https://blog.jetbrains.com/idea/2016/07/java-8-top-tips/
http://blog.agiledeveloper.com/2015/06/lambdas-are-glue-code.html
https://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/
https://dzone.com/articles/java-8-functional-interfaces-0
http://openjdk.java.net/jeps/269
https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_java
http://www.artima.com/intv/handcuffs.html
http://www.mindview.net/Etc/Discussions/CheckedExceptions
https://github.com/jOOQ/jOOL#orgjooqlambdaunchecked
http://iteratrlearning.com/java9/2016/08/06/java9-streams.html
http://www.javaslang.io/
http://www.scala-lang.org/
That’s all folks!

Writing beautiful code with Java 8

  • 1.
    Writing beautiful code withJava 8 sergiu.indrie@iquestgroup.com
  • 2.
    Disclaimer This is nota clean code presentation, but rather a code esthetics oriented presentation which may include clean code.
  • 3.
    Beautiful code? ● Easyto read/understand/write ● Concise ● DSL-like ● Clean code ++
  • 4.
    Ugly vs Beautiful for(int i = 0; i < meetings.size(); i++) { System.out.println(meetings); } for (Meeting meeting : meetings) { System.out.println(meeting); } meetings.forEach(System.out::println);
  • 5.
    Ugly vs Beautiful newThread(new Runnable() { @Override public void run() { System.out.println("Complex stuff"); } }).start(); new Thread(() -> System.out.println("Complex stuff")).start();
  • 6.
    Ugly vs Beautiful Map<String,List<Meeting>> meetingsById = meetings.stream() .collect(Collectors.groupingBy(Meeting::getId)); Map<String, List<Meeting>> meetingsGrouped = new HashMap<>(); for (Meeting meeting : meetings) { if (!meetingsGrouped.containsKey(meeting.getId())) { meetingsGrouped.put(meeting.getId(), new ArrayList<>()); } meetingsGrouped.get(meeting.getId()).add(meeting); }
  • 7.
    Ugly vs Beautiful //guarded logging if (logger.isDebugEnabled()) { logger.debug("This {} and {} with {} ", 1, that, compute()); } VS logger.debug("This {} ", () -> compute());
  • 8.
    What’s “new” inJava 8? ● Lambdas Runnable r2 = () -> System.out.println("Hello world two!"); ● Streams List<Room> rooms = microsoftExchangeService.getRoomLists().getItems().parallelStream() .filter(this::isValidRoomList) .map(this::retrieveRoomsInRoomList) .flatMap(List::stream) .collect(Collectors.toList()); ● Optional Optional<Meeting> meeting = meetingsDao.findById(meetingId); meeting.ifPresent(this::setMeetingAsManuallyEnded);
  • 9.
    PS - Helpfrom IDEA ● Migration suggestions (more to come in IDEA 2016.3)
  • 10.
    Enemy #1: CheckedExceptions private static void checkedException() { List<String> strings = Arrays.asList(1, 2, 3, 4, 5).stream() .map(Exceptions::intToString) .collect(Collectors.toList()); System.out.println(strings); } private static String intToString(Integer number) throws Exception { if (number == 3) { throw new Exception("wrong number, pal!"); } return String.valueOf(number); }
  • 11.
    Enemy #1: CheckedExceptions ● Complex issue (see Brian Goetz’s post from 2010) ○ generic type parameters are monadic ⇒ one exact type ○ throws clauses are variadic ⇒ 0 or more types ● Solution?
  • 12.
    Enemy #1: CheckedExceptions - Solution ● 1st Solution - Unchecked Exceptions* ● 2nd Solution - Wrap to 1st (see org.jooq.lambda.Unchecked) public static <T> T unchecked(Callable<T> callable) { try { return callable.call(); } catch (Exception e) { throw new RuntimeException(e); } } List<Room> rooms = roomAddresses.parallelStream() .map(room -> unchecked(() -> getRoomWithoutMeetings(room))) .collect(Collectors.toList()); * Python, Scala, C#, Ruby, PHP … don’t have checked exceptions
  • 13.
    Enemy #1: CheckedExceptions - Solution public static <T> T unchecked(Callable<T> callable) { try { return callable.call(); } catch (ApiServiceException e) { throw new ApiServiceRuntimeException(e); } catch (Exception e) { throw runtime(e); } } private static RuntimeException runtime(Throwable e) { if (e instanceof RuntimeException) { return (RuntimeException) e; } return new RuntimeException(e); }
  • 14.
    Enemy #1: CheckedExceptions - Solution ● A more functional approach
  • 15.
    Enemy #1: CheckedExceptions - Solution ● A more functional approach String complexResult = Try.of(SomeClass::dangerousGet) .recover(x -> Match(x).of( Case(instanceOf(IllegalStateException.class), () -> "1st exception"), Case(instanceOf(IllegalArgumentException.class), () -> "2nd exception") )) .getOrElse("default2");
  • 16.
    By the way Java9 brings: Collection Factory Methods* (all immutable) + some stream improvements like iterate, take/dropWhile * Nevermind if you’ve been using Guava, jOOQ
  • 17.
    Java 8 isnice, but don’t // long lambdas numbers.forEach(e -> { int count = 0; for(int i = 1; i <= e; i++) { if(e % i == 0) count++; } System.out.println(count); }); // unformatted streams List<String> strings = Arrays.asList(1, 2, 3).stream().map(Object::toString) .map(String::toUpperCase).limit(5).collect(Collectors.toList());
  • 18.
    Java 8 isnice, but Java 8 Computation Style
  • 19.
    Level up: Javaslang //Java 8 List<Integer> integers = Arrays.asList(1, 2, 3, 4); List<Integer> evenNumbers = integers.stream() .filter(nr -> nr % 2 == 0) .collect(Collectors.toList()); // Javaslang List<Integer> integers = List.of(1, 2, 3, 4); List<Integer> evenIntegers = integers.filter(nr -> nr % 2 == 0); * javaslang.collection.List
  • 20.
    Level up: Javaslang ●“...greatly inspired by Scala” ● Hence very functional ● facilitates functional programming through immutability
  • 21.
    List<Integer> integers =Arrays.asList(1, 2, 3, 4); ● List is really javaslang.collection.List :) but we do have toJavaList/Array/Collection/Set() etc. public interface List<T> extends Kind1<List<?>, T>, LinearSeq<T>, Stack<T> { default java.util.List<T> toJavaList() { return ValueModule.toJavaCollection(this, new ArrayList<>()); } ● All Javaslang collections are Iterable and thus can be used in enhanced for-statements for (String s : List.of("Java", "Advent")) { // side effects and mutation } Level up: Javaslang
  • 22.
    ● Functional exceptionhandling // no need to handle exceptions Try.of(SomeClass::bunchOfWork).getOrElse("default"); String complexResult = Try.of(SomeClass::dangerousGet) .recover(x -> Match(x).of( Case(instanceOf(IllegalStateException.class), () -> "1st exception"), Case(instanceOf(IllegalArgumentException.class), () -> "2nd exception") )) .getOrElse("default2"); Level up: Javaslang
  • 23.
    ● Lazy Lazy<Double> lazy= Lazy.of(Math::random); lazy.isEvaluated(); // = false lazy.get(); // = 0.123 (random generated) lazy.isEvaluated(); // = true lazy.get(); // = 0.123 (memoized) ● + other FP features like function composition, currying, memoization, lifting, immutable collections, tuples Level up: Javaslang
  • 24.
    Or maybe justswitch to Scala :D // type inference, nice constructors, native streams API, no semicolons :) val integers = List(1, 2, 3, 4) val evenIntegers = integers.filter(_ % 2 == 0) // pre/post/infix operators val sum = (1 to 10).sum // immutable, generated equals/getter/toString/hashcode, pattern matching decomposition case class Person(firstName: String, lastName: String) object Singleton {} // immutable collections, XML processing, multiple inheritance, tuples, REPL etc.
  • 25.
    Or maybe justswitch to Scala :D // pattern matching & decomposition object Demo { def main(args: Array[String]) { val alice = new Person("Alice", 25) val charlie = new Person("Charlie", 32) for (person <- List(alice, charlie)) { person match { case Person("Alice", 25) => println("Hi Alice!") case Person(name, age) => println( "Age: " + age + " year, name: " + name + "?") } } } case class Person(name: String, age: Int) }
  • 26.
  • 27.