Craig Larman
Before we go into details ... Explain quote What does he really mean: idioms and technique are what is important.Adopt-a-JSR
Community involvement and community driven standards. Poll if people have heard of it?Concrete Examples focus discussion
A lot of the mistakes people made were similar similar to the mistakes I made, know several java champs similar mistake Conclusion: other people not at hackdays "We're not so different you and I"
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println("button clicked");
    }
});
                    
                    
                    
                        existing code as data solution
                         aim: register a callback for when someone clicks a button
                         familiar with anonymous inner classes
                         bulky and verbose
                    
button.addActionListener(
    ?
);
                    
                    
                        outer method call is fine
                         issue is what goes in the middle, something that represents an action.
                    
button.addActionListener(event
);
                    
                    
                        well we know the that we're passing in something
                        that gets given an event as a parameter
                    
button.addActionListener(event ->
    System.out.println("button clicked")
);
                    
                     previously required the type of the event
                         can be inferred from context
                         already in java 7: diamond operator
                    
Runnable helloWorld =
    () -> System.out.println("Hello World");
                    
                    
                        also possible to infer the type from an assignment
                         no arguments variant of a lambda
                    
String name = getUserName();
button.addActionListener(event ->
    System.out.println("hi " + name)
);
                    
                    
                        improvement over final variable
                         capturing non-final variables that are 'effectively final'.
                    
public interface ActionListener extends EventListener {
    public void actionPerformed(ActionEvent event);
}
                    
                    
                        here's the example method from earlier
                         single abstract method
                    
int count = 0;
for (Artist artist : artists) {
    if (artist.isFrom("London")) {
        count++;
    }
}
                    
                    
                        familiar concept
                         the control flow is driven by the code performing the operation
                         can't do parallel iteration
                    
artists.stream()
       .filter(artist -> artist.isFrom("London"))
       .count();
                    
                    
                        same code rewritten to use internal iteraion
                         there's no visible iteration!
                         iteration is delegated to the stream
                         this is why its an inversion of control: stream knows how to iterate effectively.
                         you may call into streams, but they call you back, which lets them control the threading model.
                    
List<String> collected = Stream.of("a", "b", "hello")
                               .map(string -> string.toUpperCase())
                               .collect(toList());
assertEquals(asList("A", "B", "HELLO"), collected);
                    
                    
                        in code
                         Explain Stream.of
                         explain general form
                         point out the toUpperCase()
                    
int sum = Stream.of(1, 2, 3, 4)
                .reduce(0, (acc, x) -> acc + x);
assertEquals(10, sum);
                    
                    
                        remember to mention the sum()
                    
List<String> beginningWithNumbers =   
    Stream.of("a", "1abc", "abc1")
          .filter(value -> isDigit(value.charAt(0)))
          .collect(toList());
assertEquals(asList("1abc"), beginningWithNumbers);
                    
                    
                        retain only strings whose first character is a digit.
                    for a given an album, find the nationality of every band playing on that album
go through a worked example of how to put together a pipeline of stream operations.
List<String> origins =
  album.getMusicians()
       .filter(artist -> artist.getName().startsWith("The"))
       .map(artist -> artist.getNationality())
       .collect(toList());
                    
                    
                        make joke about bands starting with the
                        
str -> str.length
String::length
x -> foo.bar(x)
foo::bar
str -> new Name(str)
Name::new
                    
                    
                        one more note about introductory stuff
                    
// Originally invalid
Stream.of(1, 2, 3)
      .forEach(x -> System.out.println(x));
// Required extra ;
Stream.of(1, 2, 3)
      .forEach(x -> System.out.println(x););
                    
                    
                        Tripped up loads of people
                        Incredibly confusing compiler message
                        Issue was that println returned void and was a statement
                        Need a ; at the end of statements
                        thankfully fixed after being reported
                    Different reactions depending on whether something is presented as a loss or a gain.
Basic Psychology "Prospect theory shows that a loss is more significant than the equivalent gain" Coming from Scala: you might expect tuples and higher kinded types Coming from Java 7 you're happy with what's provided Also results in the general reaction from Scala devs that they aren't coming back
List<String> origins =
  album.getMusicians()
       .filter(artist -> artist.getName().startsWith("The"))
       .map(artist -> artist.getNationality())
       .collect(toList());
                    
                    
                        Recall from earlier
                    
  album.getMusicians()
       .filter(artist -> artist.getName().startsWith("The"))
       .map(artist -> artist.getNationality())
// What's happened?
       .collect(toList());
                    
                    
                        does nothing
                         Streams are really builders for collections
                         people not at all understanding eager vs lazy was a common mistake
                         nothing is constructed untill the last call
                         just building up a recipe
                         key trick: anything that returns a Stream is lazy
                    Maybe ...
list.stream()
    .map(x -> 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(y, x)))
    .collect(toList());
                    
                    
                        Issue is that lambda expressions aren't reference-able.
                         Say your lambda expression interacts with a database: how do you mock it?
                         Anyone spot the subtle bug?
                         atan2(0,0) is mathematically undefined, Java gives an answer
                         NaN return, +ve/-ve 0, 10 infinity related special cases
                    
double complexFunction(double x) {
    return 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(0, x));
}
list.stream()
    .map(this::complexFunction)
    .collect(toList());
                    
                    
                        extract your lambda out to a function if its complex.
                         Still get the code reuse and stream functionality through method references
                         remember to explain what a method reference is.
                         Method references really help testability for things like this.
                    
// Streams
list.stream()
    .filter(filteringFunction)
    .map(mappingFunction)
    .collect(toList());
// Ye olde for loop
List<Bar> bars = new ArrayList<>();
for (Foo element : list) {
    if (filteringFunction(element) {
        Bar result = mappingFunction(element);
        bars.add(result);
    }
}
                    
                    
                        Issue is understanding intermediate values.
                    
list.stream()
    .filter(filteringFunction)
    .peek(e -> System.out.println("Filtered value: " + e));
    .map(mappingFunction)
    .map(e -> e);
    .collect(toList());
                    
                    
                        could also be logging output or debugger breakpoint.
                         similar to the unix tee program.  Hands up.
                    
Comparator<String> comparator = comparing(String::length);
Comparator<String> comparator = comparing(str -> str.length);
                    
                    
                        Comparator is a functional interface.
                         provides some static methods for comparing by keys
                         explanatory example
                    
java: reference to comparing is ambiguous both
method
<T>comparing(java.util.function.ToIntFunction< ? super T>)
in java.util.Comparator and method
<T,U>comparing(java.util.function.Function< ? super T,? extends U>)
in java.util.Comparator match
                    
                    
                        Overload resolution failure.
                         current state of play, but not likely to have a language fix
                         may change libraries
                    
// Generic object variant
public static <T, U extends Comparable< ? super U>>
    Comparator<T>
        comparing(Function< ? super T, ? extends U> keyExtractor)
// Specialised primitive variant
public static <T>
    Comparator<T>
        comparing(ToIntFunction< ? super T> keyExtractor)
                    
                    
                        overloads are an issue
                         explain primitive specialised variant
                         overloads + type inference can be a bad combo, quite unusual
                         recommendation to library writers: don't overload on functional interfaces
                    Thinking in terms of the input to output relationship and not a sequence of steps
What is it? Functional means different things to different communities clojure: immutability, haskell: purity + monads, scala: a series of nouns which can be used for trolling. Biggest issue that I saw: easy to develop and understand, but takes a bit of practise. In the same way that patterns such as SOLID exist in OOP and take a while to develop.
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.forEach(x -> {
    System.out.println(x);
});
                    
                    
                        Pretty consistent in understanding internal vs external iteration.
                         not initially trying higher order functions.
                    Eg: capture non-final local variables
A lot of people want features which aren’t in the direction of java 8 doesn't make parallelism easier.Count the number of instances of each word in a document.
Example problem from the devoxx uk hackday nominally simple problem Required understanding of the collectors abstraction. Given a reader as input.
reader.lines()
      .flatMap(s -> s.splitAsStream(" "))
      .collect(groupingBy(s -> s, 
               counting()));
                    
                    
                        Explain code
                         explain the breakdown
                         emphasize the downstream collectors
                    
reader.lines()
      .flatMap(s -> Stream.of(s.split(" ")))
      .collect(groupingBy(s -> s,
               reducing(s -> 1, Integer::sum)));
// Map entries for "dad"
// [ "dad", "dad", "dad" ] => [1, 1, 1] => 3
                    
                    
                        Counting not originally there.
                         Subsequently added.
                         People not only failed to get there, but also failed to understand the solution when presented
                    
Map<String, List<String>> initial
      = br.lines()
          .flatMap(s -> Arrays.stream(s.split(" ")))
          .collect(groupingBy(s -> s));
  
  Map<Map.Entry<String, Integer>, Integer> freq1 = initial
    .entrySet().stream()
    .map(entry -> new AbstractMap.SimpleImmutableEntry<String,
Integer>(entry.getKey(), entry.getValue().size()))
    .collect(Collectors.toMap(entry -> entry.getValue()));
                    
                    
                        nearly right to begin with
                        building the map up
                        doesn't realise about downstream collectors
                    
  Supplier<HashMap<String, Integer>> supplier = () -> new
HashMap<String, Integer>();
  BiConsumer<HashMap<String, Integer>, Map.Entry<String, Integer>> accum =
    (HashMap<String, Integer> result, Map.Entry<String, Integer>
entry) -> result.put(entry.getKey(), entry.getValue());
  BiConsumer<HashMap<String, Integer>, HashMap<String, Integer>>
merger = HashMap::putAll;
  Map<String, Integer> freq2 = initial.entrySet().stream()
    .map(entry -> new AbstractMap.SimpleImmutableEntry<String,
Integer>(entry.getKey(), entry.getValue().size()))
    .collect(supplier, accum, merger);
                    
                    
                        downhill from there, don't worry about understanding this code
                         make point of weird approach if things start looking like this
                         also, choose first steps wisely
                         sometimes indicates a lack of library knowledge
                         you'll write something like this, treat it as a learning experience not "FP is unreadable."
                    @RichardWarburto