//********************************************************************// //**************Lambda Expressions - November 4th, 2016**************// //******************************************************************// -To recap: iterators "decouple" the iteration of a data structure from said structure's internal details, and are implemented via the "Iterable" interface. -Java 8 added "Lambda expressions," which are so new that you probably will still find professional programmers who don't use them yet. -Let's go back to Comparators. Let's say we only use the Comparator interface in ONE PLACE- we can just create and implement a new interface implementation right there by making an ANONYMOUS INNER CLASS: e.g.: "..., new Comparator<Trooper>() { public int Compare(Trooper a, Trooper b) { if(a.hasMustache() && !b.hasMustache()) { return 1; } else if(b.hasMustache() && !a.hasmustache()) { return -1; } else {return a.compareTo(b)} } }" -"ANONYMOUS" since it doesn't have a name -BEFORE we get to Lambda expressions, though, we have "Functional Interfaces" - interfaces with just a SINGLE, ABSTRACT method (not counting inherited methods). -Occasionally abbreviated "SAM" - single abstract method type -Like @Override, we can use "@FunctionalInterface" to have the compiler check if something is a functional interface Functional Interfaces are nice because they're easy to define via Anoymous Inner Classes, but we can do even better: a LAMBDA EXPRESSION -A LAMBDA EXPRESSION is a shortcut Java gives us to define the abstract method of a functional interface AND instantiates an abstract class for it -Defined like THIS: (T1 x1, T2 x2, ... , Tn xn) -> {method_body} -This is useful because, over time, it gets rid of a LOT of boilerplate code -However, (for now), they're just "syntax sugar" - they don't let us do anything new, they just make it easier to type -If the "method body" is just ONE LINE, we can ommit the curly braces -Let's say we have a method: static interface Bar { int compare (Trooper a, Trooper b); } static void foo(Bar b) {...} -We can put the lambda expression INSIDE the call for Foo, e.g.: "foo((Trooper a, Trooper b) -> { if...(same code as for AIC example)... });" -This creates an instance of the "Bar" interface using the lambda expression -How does Java know it's a "Bar"? Because the TARGET TYPE of "foo"'s method header ONLY takes Bar, the compiler uses TARGET TYPING to figure out that the Lambda Expression has to be a Bar -If we had, say, 2 "foo()" methods that both took 1 parameter with different types, then Java couldn't figure out which type the Lambda Expression should be and would throw an error -Could get around this by actually casting the Lambda Expression itself to "Bar" -Could also bind the Lambda Expression to a variable with the desired type, e.g.: Comparator<Trooper> compy = (Trooper a, Trooper b) -> 1; -So, a Lambda Expression is fundamentally a notation for creating an AIC as quickly as possible -A METHOD REFERENCE (need to look this up) -Let's say we have a functional interface "foo" with a method "String bar(object x);" e.g. doo(System.out::println) IS THE SAME AS doo(x -> System.out.println(x)) -3 kinds of method references: -"object"::"instanceMethod" - - -By using method references in combination with existing methods, it's possible to get EXTREMELY condensed, very nice-reading code (example in slides) -SIDE NOTE: -Top-Down Development: -Define the broad structure of the program (e.g. method headers), then implement all the methods / pieces to get it actually working -Bottom-Up Development -Make all the pieces, then stitch them together -Different projects/ languages are more suited for each