Upload
sergiu-mircea-indrie
View
54
Download
2
Embed Size (px)
Citation preview
Writing beautiful code with Java 8
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?
● LambdasRunnable r2 = () -> System.out.println("Hello world two!");
● StreamsList<Room> rooms = microsoftExchangeService.getRoomLists().getItems().parallelStream()
.filter(this::isValidRoomList)
.map(this::retrieveRoomsInRoomList)
.flatMap(List::stream)
.collect(Collectors.toList());
● OptionalOptional<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-java8https://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.htmlhttps://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/https://dzone.com/articles/java-8-functional-interfaces-0http://openjdk.java.net/jeps/269https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_javahttp://www.artima.com/intv/handcuffs.htmlhttp://www.mindview.net/Etc/Discussions/CheckedExceptionshttps://github.com/jOOQ/jOOL#orgjooqlambdauncheckedhttp://iteratrlearning.com/java9/2016/08/06/java9-streams.htmlhttp://www.javaslang.io/http://www.scala-lang.org/
That’s all folks!