functional uis with java 8 and vaadin javaone2014
DESCRIPTION
Slides for the JavaOne 2014 talk about how the functional features of Java 8 and Vaadin empower you to write clearer UI code.TRANSCRIPT
MissionMissionWhy do we exist
Make building amazing web applications easy
Build UIs with components
Vaadin X
Developer
Productivity
Rich
UX
Web application layers
JavaScriptWeb serverBackend Communication
JS required required required requiredVaadin
required optionalrequired optional
Web application layers
JavaScriptWeb serverBackend Communication
JS required required required required
Vaadin
required optionalrequired optional
1 layer vs
3 layers
Less code Less bugs Faster time-to-market
But can it scale?
Test results
20,622 AJAX requests / minute before exceeding 1% rejected connections
MPAA reports 1,4 billion movie tickets sold in 2009. !
~2,700 tickets / minute.
5,496 tickets / minute
~11,000 concurrent users
On a single Amazon EC2 Large instance
www.vaadin.com/blog/-/blogs/vaadin-scalability-study-quicktickets
Wrong, but the community is very active there
> 100.000 developers from > 10.000 cities > 450 add-ons in the marketplace
Other 4 %Asia
20 %
Americas 22 %
Europe 54 %
Open Source community
Apache-licensed
Demo time
github.com/hezamu/WorkoutTracker
What is Functional Programming?
A style of programming that expresses computation as the evaluation of mathematical functions
Recursion
Lazy evaluation
Lambda expressionsType theory
MonadsReferential transparency
Currying
Entscheidungsproblem
Pattern matching
Tuples
Something practical?
Side effects?
State?
Denied
Denied
Okay…
What’s in it for me?
A new way of thinking
A new way of programming
Write tight, robust and scalable code
What’s hot in Java 8
Improved Date API
New Java 8 Date API in action
public int monthAge() { return (new Date().getYear() - date.getYear()) * 12 + (new Date().getMonth() - date.getMonth()); }
// Java 8 version with the new Date API public int monthAge() { return (int) Period.between(date, LocalDate.now()).toTotalMonths(); }
Lambda expressions
Anonymous functions
Runnable r = () -> System.out.println("hello lambda!”);
Comparator<Integer> cmp1 = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);
// Anonymous onsite functions button.addClickListener(event -> System.out.println("Button clicked!"));
Comparator<Integer> cmp2 = (x, y) -> { return (x < y) ? -1 : ((x > y) ? 1 : 0); // Need return if not one liner };
Workout Tracker example
editor.clear.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { editor.clearValues(); updateRating(); } });
// Java 8 version with a lambda editor.clear.addClickListener(event -> { editor.clearValues(); updateRating(); });
Method references with the :: notation
! private void eventHandler(Button.ClickEvent event) { // do something about the button click }
button.addClickListener(this::eventHandler);
// If the handler method is static button.addClickListener(MyClass::eventHandler);
Workout Tracker example
!editor.activity.addValueChangeListener(new Property.ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { updateRating(); } });
editor.date.addValueChangeListener(this::updateRating);
Streams
Composable to new Streams with higher order functions
Stream != Collection
As lazy as possible
Create from a Collection or an Iterable
Input validation
private boolean areInputsValid() { Component component = null; for (Iterator<Component> iter = editor.iterator(); iter.hasNext(); iter.next()) { if (fieldNotValidating(component)) return false; } return true; }
// Java 8 version with anyMatch and a method reference private boolean areInputsValid() { return !StreamSupport.stream(editor.spliterator(), true) .anyMatch(this::fieldNotValidating); }
From rating to stars
StringBuilder stars = new StringBuilder("New Workout: "); for (int i = 0; i < rating; ++i) { stars.append(FontAwesome.STAR.getHtml()); } editor.title.setValue("New Workout: “ + stars);
String stars = IntStream.range(0, rating) .mapToObj(r -> FontAwesome.STAR.getHtml()) .collect(Collectors.joining("")); editor.title.setValue("New Workout: " + stars);
Higher order functions
A function that takes one or more functions as input
Returns a new stream by applying the given function to all elements of this stream. !
MapReturns a new stream consisting of the elements of this stream that match the given predicate.
Filter
SQL analogue: SELECT SQL analogue: WHERE
Workout Tracker Example
!
!
!
private List<Workout> findByAge(int maxMonths) { List<Workout> result = new ArrayList<>(); for (Workout w : workouts) { if (w.monthAge() < maxMonths) { result.add(w); } } Collections.sort(result, new Comparator<Workout>() { @Override public int compare(Workout o1, Workout o2) { return o2.monthAge() - o1.monthAge(); } }); ! return result; }
!
!
!
private Stream<Workout> findByAge(int maxMonths) { return workouts.stream() .filter(w -> w.monthAge() < maxMonths) .sorted(Comparator.comparing(Workout::monthAge) .reversed()); }
What is Functional Reactive Programming?
Observable
Consider a spreadsheet
RxJava
Quick peek at RxJava
Observable<String> letters = Observable.from(new String[] { "a", "b", "c" });
letters.subscribe(letter -> System.out.println("Got letter: " + letter));
Power of Observables - composition
Observable<String> letters = Observable.from(new String[] { "a", “b", "c" }); Observable<Integer> numbers = Observable.from(new Integer[] { 1, 2, 3 });
Observable<String> pairs = Observable.combineLatest(letters, numbers, (l, n) -> { return "" + l + " -> " + n; }); !pairs.subscribe(pair -> System.out.println("Got pair: " + pair));
RxVaadinwww.vaadin.com/blog/-/blogs/reactive-functional-ui-development-with-vaadin
Rating to stars, reactive style!
Observable<String> activities = RxVaadin.valuesWithDefault(editor.activity, null); Observable<String> durations = RxVaadin.valuesWithDefault(editor.duration, ""); Observable<Date> dates = RxVaadin.valuesWithDefault(editor.date, editor.date.getValue()); Observable<String> calories = RxVaadin.valuesWithDefault(editor.calories, ""); Observable<String> avgHRs = RxVaadin.valuesWithDefault(editor.avgHR, ""); Observable<String> maxHRs = RxVaadin.valuesWithDefault(editor.maxHR, ""); Observable<String> comments = RxVaadin.valuesWithDefault(editor.comment, "");
Composing the rating Observable
Observable<Integer> ratings = WorkoutRatingLogic.ratings(activities, durations, dates, calories, avgHRs, maxHRs, comments);
Observable<String> ratingStrings = ratings.map(rating -> { if (rating == null) { return "New Workout”; // No stars if required fields not ok } else { return IntStream.range(0, rating) .mapToObj(i -> FontAwesome.STAR.getHtml()) .collect(Collectors.joining("", "New Workout: ", "")); } });
Last step: update the UI
// Have the label update its value whenever the Observable // emits a value RxVaadin.follow(editor.title, ratingStrings);
// Disable or enable the add button based on if the rating // calculation was successful or not ratings.subscribe(rating -> editor.add.setEnabled(rating != null));
Summary
The functional additions in Java 8 rock
Observables allow us avoid the “callback hell” with events
Vaadin works brilliantly with both
!
Join us for a chat and drinks at the Vaadin Meetup
!
Hotel Serrano
Right now!