Programming II

undefined
 
Programming II
 
O
b
j
e
c
t
 
O
r
i
e
n
t
e
d
P
r
o
g
r
a
m
m
i
n
g
 
w
i
t
h
 
J
a
v
a
-
 
A
d
v
a
n
c
e
d
 
T
o
p
i
c
s
 
-
 
J
a
v
a
 
8
:
 
F
u
n
c
t
i
o
n
a
l
 
I
n
t
e
r
f
a
c
e
s
,
M
e
t
h
o
d
 
R
e
f
e
r
e
n
c
e
s
 
a
n
d
L
a
m
b
d
a
 
E
x
p
r
e
s
s
i
o
n
s
 
Alastair Donaldson
www.doc.ic.ac.uk/~afd
Remember good old 
map
 from Haskell?
map :: (a -> b) -> [a] -> [b]
 
 
 
Prelude> map (\x -> 10*x) [0..9]
[0,10,20,30,40,50,60,70,80,90]
 
 
 
Prelude> map (\x -> 1.0*x) [0..9]
[0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]
 
 
 
Prelude> map (\x -> [x-1, x, x+1]) [0..9]
[[-1,0,1],[0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],
[6,7,8],[7,8,9],[8,9,10]]
Let’s try to do this in Java
Multiply each list element by 10
Convert each list element to a float
Raise list element x to a three-element list [x-1, x, x + 1]
Attempt 1: three separate loops
public class JustLoops {
 
  public static void main(String[] args) {
 
    List<Integer> integers = new ArrayList<Integer>();
    for(int i = 0; i < 10; i++) {
      integers.add(i);
    }
 
    List<Integer> tenTimesBigger = new ArrayList<Integer>();
    for(Integer i : integers) {
      tenTimesBigger.add(10*i);
    }
    System.out.println(tenTimesBigger);
 
    List<Float> floats = new ArrayList<Float>();
    for(Integer i : integers) {
      floats.add(new Float(i));
    }
    System.out.println(floats);
Make the list
[0..9]
Multiply
elements
by ten
Turn
elements
into floats
Attempt 1: three separate loops
    ...
 
    List<List<Integer>> triples = new ArrayList<>();
    for(Integer i : integers) {
      triples.add(Arrays.asList(new Integer[] { i-1, i, i+1 }));
    }
    System.out.println(triples);
  }
 
}
Raise each
element to a
triple
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
[[-1, 0, 1], [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
[5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
Program output:
Critique of Attempt 1
 
Produces the same effect as the Haskell program
 
…but the 
map
 logic is re-implemented each time
 
Not satisfactory
 
Attempt 2: a Transformer interface
 
Haskell’s 
map
 takes a function that transforms something of
type 
a
 into something of type 
b
:
 
map :: (a -> b) -> [a] -> [b]
 
L
e
t
s
 
w
r
i
t
e
 
m
a
p
 
i
n
 
J
a
v
a
,
 
i
n
 
t
e
r
m
s
 
o
f
 
a
n
 
i
n
t
e
r
f
a
c
e
 
t
h
a
t
 
c
a
n
t
r
a
n
s
f
o
r
m
 
s
o
m
e
t
h
i
n
g
 
o
f
 
t
y
p
e
 
a
 
i
n
t
o
 
s
o
m
e
t
h
i
n
g
 
o
f
 
t
y
p
e
 
b
 
…but we’re in Java mode, so we’ll use 
S
 and 
T
, not 
a
 and 
b
Transformer interface
public interface Transformer<S, T> {
    public T transform(S x);
}
 
Let us write our three transformers
T
h
e
 
i
n
t
e
r
f
a
c
e
 
i
s
 
g
e
n
e
r
i
c
 
w
i
t
h
r
e
s
p
e
c
t
 
t
o
 
t
y
p
e
s
 
S
 
a
n
d
 
T
transform
 says: “give me
an 
S
 and I will give you back
a 
T
Three Transformer implementations
public class TimesTenTransformer
    implements Transformer<Integer, Integer> {
  @Override
  public Integer transform(Integer x) {
    return x*10;
  }
}
public class IntegerToFloatTransformer
    implements Transformer<Integer, Float> {
  @Override
  public Float transform(Integer x) {
    return new Float(x);
  }
}
public class IntegerToTripleTransformer
    implements Transformer<Integer, List<Integer>> {
  @Override
  public List<Integer> transform(Integer x) {
    return Arrays.asList(new Integer[] { x - 1, x, x + 1 });
  }
}
N
o
t
i
c
e
 
h
e
r
e
 
t
h
a
t
 
w
e
p
r
o
v
i
d
e
 
c
o
n
c
r
e
t
e
t
y
p
e
s
 
f
o
r
 
S
 
a
n
d
 
T
w
h
e
n
 
w
e
i
m
p
l
e
m
e
n
t
T
r
a
n
s
f
o
r
m
e
r
Implementing 
map
 using the
Transformer
 interface
public interface Transformer<S, T> {
 
  public T transform(S x);
 
  public static <A, B>
  List<B> map(Transformer<A, B> transformer, List<A> input) {
    List<B> result = new ArrayList<>();
    for(A a : input) {
      result.add(transformer.transform(a));
    }
    return result;
  }
 
}
 
I
n
 
J
a
v
a
 
8
,
 
a
n
 
i
n
t
e
r
f
a
c
e
 
c
a
n
 
h
a
v
e
 
s
t
a
t
i
c
 
m
e
t
h
o
d
s
 
This is good, because sometimes an interface is the most
logical home for such a method
 
Our 
map
 method is not specific to any particular
transformer, so it makes sense for it to live in the interface
Using map with our transformers
public class UsingInterfaces {
  public static void main(String[] args) {
 
    List<Integer> integers = new ArrayList<Integer>();
    for(int i = 0; i < 10; i++) {
      integers.add(i);
    }
 
    List<Integer> tenTimesBigger = Transformer.map(
                      new TimesTenTransformer(), integers);
    System.out.println(tenTimesBigger);
 
    List<Float> floats = Transformer.map(
                      new IntegerToFloatTransformer(), integers);
    System.out.println(floats);
 
    List<List<Integer>> triples = Transformer.map(
                      new IntegerToTripleTransformer(), integers);
    System.out.println(triples);
  }
 
}
Critique of approach 2
 
We succeeded in capturing the essence of 
map
 
Generics played a nice role in making this work
 
P
a
i
n
f
u
l
:
 
w
r
i
t
i
n
g
 
a
 
s
e
p
a
r
a
t
e
 
c
l
a
s
s
 
e
a
c
h
 
t
i
m
e
 
w
e
 
n
e
e
d
 
a
t
r
a
n
s
f
o
r
m
e
r
.
Especially if we need a simple transformer and we only
need it once
Approach 3: anonymous classes
Instead of declaring 
TimesTenTransformer
 as a class:
public class TimesTenTransformer
    implements Transformer<Integer, Integer> {
  @Override
  public Integer transform(Integer x) {
    return x*10;
  }
}
…and then using this class:
List<Integer> tenTimesBigger = Transformer.map(
                      new TimesTenTransformer(), integers);
…we can declare the class at the point we wish to use it.
 
W
e
 
d
o
n
t
 
e
v
e
n
 
n
e
e
d
 
t
o
 
g
i
v
e
 
t
h
e
 
c
l
a
s
s
 
a
 
n
a
m
e
:
 
i
t
 
c
a
n
 
b
e
a
n
o
n
y
m
o
u
s
An anonymous version of
TenTimesTransformer
T
h
i
s
 
d
e
c
l
a
r
e
s
 
a
 
n
a
m
e
l
e
s
s
 
c
l
a
s
s
 
t
h
a
t
i
m
p
l
e
m
e
n
t
s
 
t
h
e
 
T
r
a
n
s
f
o
r
m
e
r
 
i
n
t
e
r
f
a
c
e
,
a
n
d
 
c
r
e
a
t
e
s
 
a
 
s
i
n
g
l
e
 
i
n
s
t
a
n
c
e
 
o
f
 
t
h
i
s
 
c
l
a
s
s
List<Integer> tenTimesBigger = Transformer.map(
                      new Transformer<Integer, Integer>() {
                        @Override
                        public Integer transform(Integer x) {
                          return x*10;
                        }
                      }
                      , integers);
An anonymous version of
IntegerToFloatTransformer
Says:
 
“Make an instance of a class that 
 
implements the
 
Transformer<Integer, Float>
 interface, by
 
defining the 
transform
 method as follows…”
 List<Float> floats = Transformer.map(
                      new Transformer<Integer, Float>() {
                        @Override
                        public Float transform(Integer x) {
                          return new Float(x);
                        }
                      }
                      , integers);
An anonymous version of
IntegerToTripleTransformer
Similar:
 List<List<Integer>> triples = Transformer.map(
 
        new Transformer<Integer, List<Integer>>() {
 
          @Override
 
          public List<Integer> transform(Integer x) {
 
            return Arrays.asList(new Integer[]
                      { x - 1, x, x + 1 });
 
          }
 
        }, integers);
Critique of approach 3
 
Arguably less painful than approach 2: we did not have to
write a separate class file for each transformer.
 
Instead we described the transformers “on demand” using
anonymous classes.
 
But the anonymous class syntax is pretty verbose!
Functional interfaces
 
A
n
 
i
n
t
e
r
f
a
c
e
 
i
s
 
a
 
f
u
n
c
t
i
o
n
a
l
 
i
n
t
e
r
f
a
c
e
 
i
f
 
i
t
 
d
e
c
l
a
r
e
s
 
e
x
a
c
t
l
y
o
n
e
 
a
b
s
t
r
a
c
t
 
m
e
t
h
o
d
.
 
To implement a functional interface, a class just needs to
provide the single abstract method.
 
Transformer
 is a functional interface:
-
One abstract method: 
transform
-
Also one non-abstract, static method, 
map
Functional interfaces and method
references
 
S
u
p
p
o
s
e
 
a
 
m
e
t
h
o
d
 
e
x
p
e
c
t
s
 
a
 
p
a
r
a
m
e
t
e
r
 
o
f
 
t
y
p
e
 
I
,
 
w
h
e
r
e
 
I
i
s
 
a
 
f
u
n
c
t
i
o
n
a
l
 
i
n
t
e
r
f
a
c
e
.
 
In Java 8:
-
Instead of passing an instance of a class that
implements 
I
-
y
o
u
 
c
a
n
 
p
a
s
s
 
a
 
m
e
t
h
o
d
 
t
h
a
t
 
m
a
t
c
h
e
s
 
t
h
e
 
s
i
g
n
a
t
u
r
e
 
o
f
t
h
e
 
s
i
n
g
l
e
 
a
b
s
t
r
a
c
t
 
m
e
t
h
o
d
 
a
s
s
o
c
i
a
t
e
d
 
w
i
t
h
 
t
h
e
f
u
n
c
t
i
o
n
a
l
 
i
n
t
e
r
f
a
c
e
Approach 4: use method references
 
map
 expects a 
Transformer
 as its first argument
 
Transformer
 is a functional interface
 
Instead of passing a 
Transformer<S, T>
 to 
map
, we
can pass any method that:
-
accepts a single argument of type 
S
-
returns something of type 
T
Passing timesTen as a method reference
public class UsingMethodReferences {
  private static Integer timesTen(Integer x) {
    return x * 10;
  }
  public static void main(String[] args) {
 
    List<Integer> integers = ...;
 
    List<Integer> tenTimesBigger = Transformer.map(
      UsingMethodReferences::timesTen, integers);
    System.out.println(tenTimesBigger);
 
  }
 
}
Write 
UsingMethodReferences::timesTen
 to refer to
static method 
timesTen
 of 
UsingMethodReferences
class
 
There is also syntax for getting a reference to an instance
method of an object (look it up)
Passing toTriple as a method reference
public class UsingMethodReferences {
  private static List<Integer> toTriple(Integer x) {
    return Arrays.asList(new Integer[]
      { x - 1, x, x + 1 });
  }
  public static void main(String[] args) {
 
    List<Integer> integers = ...;
 
    List<List<Integer>> triples = Transformer.map(
      UsingMethodReferences::toTriple, integers);
    System.out.println(triples);
 
  }
 
}
Method
reference
Passing Float constructor as a method
reference
public class UsingMethodReferences {
  public static void main(String[] args) {
 
    List<Integer> integers = ...;
 
    List<Float> floats = Transformer.map(
 
                Float::new, integers);
 
    System.out.println(floats);
 
  }
 
}
Float::new
 is a reference to the constructor
Float(Integer x)
T
y
p
e
 
i
n
f
e
r
e
n
c
e
 
i
s
 
u
s
e
d
 
t
o
 
d
e
t
e
r
m
i
n
e
 
w
h
i
c
h
 
c
o
n
s
t
r
u
c
t
o
r
m
a
k
e
s
 
s
e
n
s
e
 
h
e
r
e
Critique of approach 4
 
Passing in 
Float::new
 as a method reference is pretty
neat!
 
It’s not clear that passing in 
timesTen
 and 
toTriple
 as
method references is worth it.
 
They do such simple things; it’s annoying to have to write a
specially named method for each of them.
Functional interfaces and lambda
expressions
 
 
Suppose a method expects a parameter of type 
I
, where 
I
is a functional interface.
 
I
n
 
J
a
v
a
 
8
,
 
i
n
s
t
e
a
d
 
o
f
 
p
a
s
s
i
n
g
 
a
n
 
i
n
s
t
a
n
c
e
 
o
f
 
a
 
c
l
a
s
s
 
t
h
a
t
i
m
p
l
e
m
e
n
t
s
 
I
,
 
o
r
 
a
 
m
e
t
h
o
d
 
r
e
f
e
r
e
n
c
e
,
 
y
o
u
 
c
a
n
 
p
a
s
s
 
i
n
 
a
l
a
m
b
d
a
 
e
x
p
r
e
s
s
i
o
n
.
 
A lambda expression describes a function that produces a
result from some arguments.
Lambda expression: example
 
 
Instead of passing 
map
 a reference to the timesTen method:
 
 
we can instead pass a lambda expression:
 
 
Means we do not have to define 
timesTen
 at all!
List<Integer> tenTimesBigger = Transformer.map(
      UsingMethodReferences::timesTen, integers);
List<Integer> tenTimesBigger =
  Transformer.map(x -> x*10, integers);
Compactly describes a function which,
given argument 
x
, returns 
x*10
The term “lambda” comes from the Lambda Calculus
Approach 5: use lambdas
public class UsingLambdas {
  public static void main(String[] args) {
 
    List<Integer> integers = ...
 
    List<Integer> tenTimesBigger = Transformer.map(
                x -> x*10, integers);
    System.out.println(tenTimesBigger);
 
    List<Float> floats = Transformer.map(
                Float::new, integers);
    System.out.println(floats);
 
    List<List<Integer>> triples = Transformer.map(
                x -> Arrays.asList(new Integer[]
                     { x - 1, x, x + 1 })
                , integers);
    System.out.println(triples);
  }
 
}
These are lambda
expressions
 
Critique of approach 5
 
Basically gives us the elegance of map in Haskell, but in
Java.
Let’s write a method to compose two
transformers
public static <S, T, U>
Transformer<S, U> compose(
    Transformer<S, T> t1,
 Transformer<T, U> t2) {
 
    return (x -> t2.transform(t1.transform(x)));
 
}
I
n
p
u
t
:
 
a
 
t
r
a
n
s
f
o
r
m
e
r
 
f
r
o
m
 
S
 
t
o
 
T
,
 
a
 
t
r
a
n
s
f
o
r
m
e
r
 
f
r
o
m
 
T
 
t
o
 
U
O
u
t
p
u
t
:
 
t
h
e
 
t
r
a
n
s
f
o
r
m
e
r
 
f
r
o
m
 
S
 
t
o
 
U
 
o
b
t
a
i
n
e
d
 
b
y
c
o
m
p
o
s
i
n
g
 
t
h
e
 
i
n
p
u
t
 
t
r
a
n
s
f
o
r
m
e
r
s
Here we are returning a lambda expression.
The caller of 
compose
 can then apply the
lambda expression in the future
Composition in action
public class Composition {
  public static <S, T, U> Transformer<S, U> compose(
         Transformer<S, T> t1, Transformer<T, U> t2) {
    return (x -> t2.transform(t1.transform(x)));
  }
  public static void main(String[] args) {
    List<Integer> integers = ...;
 
    Transformer<Integer, List<Integer>> timesTenAndTriple =
      compose((x -> x*10),
              (x -> Arrays.asList(new Integer[]
                         { x - 1, x, x + 1 }))
             );
    List<List<Integer>> bigTriples =
                   Transformer.map(timesTenAndTriple, integers);
 
    System.out.println(bigTriples);
  }
}
Stores a reference to
a lambda expression
The stored lambda
expression is applied
 
Composition in action
Output:
 
[[-1, 0, 1], [9, 10, 11], [19, 20, 21], [29, 30, 31], [39, 40,
41], [49, 50, 51], [59, 60, 61], [69, 70, 71], [79, 80, 81],
[89, 90, 91]]
 
Summary
Anonymous classes avoid the need to declare a named
class when only a simple activity is required.
 
A
 
f
u
n
c
t
i
o
n
a
l
 
i
n
t
e
r
f
a
c
e
 
c
a
n
 
b
e
 
i
m
p
l
e
m
e
n
t
e
d
 
i
n
 
a
 
m
o
r
e
l
i
g
h
t
-
w
e
i
g
h
t
 
m
a
n
n
e
r
:
 
b
y
 
p
a
s
s
i
n
g
 
a
 
m
e
t
h
o
d
 
r
e
f
e
r
e
n
c
e
 
f
o
r
 
t
h
e
s
i
n
g
l
e
 
a
b
s
t
r
a
c
t
 
m
e
t
h
o
d
.
 
A
 
l
a
m
b
d
a
 
e
x
p
r
e
s
s
i
o
n
 
c
a
n
 
b
e
 
u
s
e
d
 
i
n
 
p
l
a
c
e
 
o
f
 
a
 
m
e
t
h
o
d
r
e
f
e
r
e
n
c
e
,
 
l
e
a
d
i
n
g
 
t
o
 
v
e
r
y
 
c
o
n
c
i
s
e
 
c
o
d
e
 
Take home message: none of this lets you do anything you
could not already do.
 
It simply allows more elegant and concise code
 
Next time
A few more details about anonymous classes
 
A discussion of default methods (a.k.a. defender methods)
in Java 8.
Slide Note
Embed
Share

In this content, we delve into advanced Java concepts such as Functional Interfaces, Method References, and Lambda Expressions. We compare Java programming techniques with those from Haskell, showcasing how to manipulate lists and elements. The exploration includes practical code examples, critiques, and an innovative approach using Transformer interfaces in Java.

  • Java Programming
  • Functional Interfaces
  • Lambda Expressions
  • Advanced Topics
  • Comparison

Uploaded on Feb 16, 2025 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.

E N D

Presentation Transcript


  1. Programming II Object Oriented Object Oriented Programming with Java Programming with Java - - Advanced Topics Advanced Topics - - Java 8: Functional Interfaces, Java 8: Functional Interfaces, Method References and Method References and Lambda Expressions Lambda Expressions Alastair Donaldson www.doc.ic.ac.uk/~afd

  2. Remember good old map from Haskell? map :: (a -> b) -> [a] -> [b] Multiply each list element by 10 Prelude> map (\x -> 10*x) [0..9] [0,10,20,30,40,50,60,70,80,90] Convert each list element to a float Prelude> map (\x -> 1.0*x) [0..9] [0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0] Raise list element x to a three-element list [x-1, x, x + 1] Prelude> map (\x -> [x-1, x, x+1]) [0..9] [[-1,0,1],[0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7], [6,7,8],[7,8,9],[8,9,10]] Let s try to do this in Java 2

  3. Attempt 1: three separate loops Make the list [0..9] public class JustLoops { public static void main(String[] args) { List<Integer> integers = new ArrayList<Integer>(); for(int i = 0; i < 10; i++) { integers.add(i); } Multiply elements by ten List<Integer> tenTimesBigger = new ArrayList<Integer>(); for(Integer i : integers) { tenTimesBigger.add(10*i); } System.out.println(tenTimesBigger); Turn elements into floats List<Float> floats = new ArrayList<Float>(); for(Integer i : integers) { floats.add(new Float(i)); } System.out.println(floats); 3

  4. Attempt 1: three separate loops ... List<List<Integer>> triples = new ArrayList<>(); for(Integer i : integers) { triples.add(Arrays.asList(new Integer[] { i-1, i, i+1 })); } System.out.println(triples); } Raise each element to a triple } Program output: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90] [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] [[-1, 0, 1], [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] 4

  5. Critique of Attempt 1 Produces the same effect as the Haskell program but the map logic is re-implemented each time Not satisfactory 5

  6. Attempt 2: a Transformer interface Haskell s map takes a function that transforms something of type a into something of type b: map :: (a -> b) -> [a] -> [b] Let s write map in Java, in terms of an interface that can transform something of type a into something of type b but we re in Java mode, so we ll use S and T, not a and b 6

  7. Transformer interface The interface is generic with respect to types S and T public interface Transformer<S, T> { public T transform(S x); } transformsays: give me an S and I will give you back a T Let us write our three transformers 7

  8. Three Transformer implementations Notice here that we provide concrete types for S and T when we implement Transformer public class TimesTenTransformer implements Transformer<Integer, Integer> { @Override public Integer transform(Integer x) { return x*10; } } implements Transformer<Integer, Float> { @Override public Float transform(Integer x) { return new Float(x); } } implements Transformer<Integer, List<Integer>> { @Override public List<Integer> transform(Integer x) { return Arrays.asList(new Integer[] { x - 1, x, x + 1 }); } } public class IntegerToFloatTransformer public class IntegerToTripleTransformer 8

  9. Implementing map using the Transformer interface public interface Transformer<S, T> { public T transform(S x); public static <A, B> List<B> map(Transformer<A, B> transformer, List<A> input) { List<B> result = new ArrayList<>(); for(A a : input) { result.add(transformer.transform(a)); } return result; } In Java 8, an interface can have static methods This is good, because sometimes an interface is the most logical home for such a method Our map method is not specific to any particular transformer, so it makes sense for it to live in the interface } 9

  10. Using map with our transformers public class UsingInterfaces { public static void main(String[] args) { List<Integer> integers = new ArrayList<Integer>(); for(int i = 0; i < 10; i++) { integers.add(i); } List<Integer> tenTimesBigger = Transformer.map( new TimesTenTransformer(), integers); System.out.println(tenTimesBigger); List<Float> floats = Transformer.map( new IntegerToFloatTransformer(), integers); System.out.println(floats); List<List<Integer>> triples = Transformer.map( new IntegerToTripleTransformer(), integers); System.out.println(triples); } } 10

  11. Critique of approach 2 We succeeded in capturing the essence of map Generics played a nice role in making this work Painful: writing a separate class each time we need a transformer. Especially if we need a simple transformer and we only need it once 11

  12. Approach 3: anonymous classes Instead of declaring TimesTenTransformer as a class: public class TimesTenTransformer implements Transformer<Integer, Integer> { @Override public Integer transform(Integer x) { return x*10; } } and then using this class: List<Integer> tenTimesBigger = Transformer.map( new TimesTenTransformer(), integers); we can declare the class at the point we wish to use it. We don t even need to give the class a name: it can be anonymous 12

  13. An anonymous version of TenTimesTransformer List<Integer> tenTimesBigger = Transformer.map( new Transformer<Integer, Integer>() { @Override public Integer transform(Integer x) { return x*10; } } , integers); This declares a nameless class that implements the Transformer interface, and creates a single instance of this class 13

  14. An anonymous version of IntegerToFloatTransformer List<Float> floats = Transformer.map( new Transformer<Integer, Float>() { @Override public Float transform(Integer x) { return new Float(x); } } , integers); Says: Make an instance of a class that implements the Transformer<Integer, Float> interface, by defining the transformmethod as follows 14

  15. An anonymous version of IntegerToTripleTransformer Similar: List<List<Integer>> triples = Transformer.map( new Transformer<Integer, List<Integer>>() { @Override public List<Integer> transform(Integer x) { return Arrays.asList(new Integer[] { x - 1, x, x + 1 }); } }, integers); 15

  16. Critique of approach 3 Arguably less painful than approach 2: we did not have to write a separate class file for each transformer. Instead we described the transformers on demand using anonymous classes. But the anonymous class syntax is pretty verbose! 16

  17. Functional interfaces An interface is a functional interface if it declares exactly one abstract method. To implement a functional interface, a class just needs to provide the single abstract method. Transformer is a functional interface: - One abstract method: transform - Also one non-abstract, static method, map 17

  18. Functional interfaces and method references Suppose a method expects a parameter of type I, where I is a functional interface. In Java 8: - Instead of passing an instance of a class that implements I - you can pass a method that matches the signature of the single abstract method associated with the functional interface 18

  19. Approach 4: use method references map expects a Transformer as its first argument Transformer is a functional interface Instead of passing a Transformer<S, T> to map, we can pass any method that: - accepts a single argument of type S - returns something of type T 19

  20. Passing timesTen as a method reference public class UsingMethodReferences { private static Integer timesTen(Integer x) { return x * 10; } public static void main(String[] args) { List<Integer> integers = ...; List<Integer> tenTimesBigger = Transformer.map( UsingMethodReferences::timesTen, integers); System.out.println(tenTimesBigger); } Write UsingMethodReferences::timesTen to refer to static method timesTen of UsingMethodReferences class } There is also syntax for getting a reference to an instance method of an object (look it up) 20

  21. Passing toTriple as a method reference public class UsingMethodReferences { private static List<Integer> toTriple(Integer x) { return Arrays.asList(new Integer[] { x - 1, x, x + 1 }); } public static void main(String[] args) { Method reference List<Integer> integers = ...; List<List<Integer>> triples = Transformer.map( UsingMethodReferences::toTriple, integers); System.out.println(triples); } } 21

  22. Passing Float constructor as a method reference public class UsingMethodReferences { public static void main(String[] args) { List<Integer> integers = ...; List<Float> floats = Transformer.map( Float::new, integers); System.out.println(floats); } } Float::new is a reference to the constructor Float(Integer x) Type inference is used to determine which constructor makes sense here 22

  23. Critique of approach 4 Passing in Float::new as a method reference is pretty neat! It s not clear that passing in timesTen and toTriple as method references is worth it. They do such simple things; it s annoying to have to write a specially named method for each of them. 23

  24. Functional interfaces and lambda expressions Suppose a method expects a parameter of type I, where I is a functional interface. In Java 8, instead of passing an instance of a class that implements I, or a method reference, you can pass in a lambda expression. A lambda expression describes a function that produces a result from some arguments. 24

  25. Lambda expression: example Instead of passing map a reference to the timesTen method: List<Integer> tenTimesBigger = Transformer.map( UsingMethodReferences::timesTen, integers); we can instead pass a lambda expression: List<Integer> tenTimesBigger = Transformer.map(x -> x*10, integers); Means we do not have to define timesTen at all! Compactly describes a function which, given argument x, returns x*10 The term lambda comes from the Lambda Calculus 25

  26. Approach 5: use lambdas public class UsingLambdas { public static void main(String[] args) { List<Integer> integers = ... List<Integer> tenTimesBigger = Transformer.map( x -> x*10, integers); System.out.println(tenTimesBigger); These are lambda expressions List<Float> floats = Transformer.map( Float::new, integers); System.out.println(floats); List<List<Integer>> triples = Transformer.map( x -> Arrays.asList(new Integer[] { x - 1, x, x + 1 }) , integers); System.out.println(triples); } 26 }

  27. Critique of approach 5 Basically gives us the elegance of map in Haskell, but in Java. 27

  28. Lets write a method to compose two transformers Input: a transformer from S to T, a transformer from T to U Output: the transformer from S to U obtained by composing the input transformers public static <S, T, U> Transformer<S, U> compose( Transformer<S, T> t1, Transformer<T, U> t2) { return (x -> t2.transform(t1.transform(x))); } Here we are returning a lambda expression. The caller of compose can then apply the lambda expression in the future 28

  29. Composition in action public class Composition { public static <S, T, U> Transformer<S, U> compose( Transformer<S, T> t1, Transformer<T, U> t2) { return (x -> t2.transform(t1.transform(x))); } public static void main(String[] args) { List<Integer> integers = ...; Stores a reference to a lambda expression Transformer<Integer, List<Integer>> timesTenAndTriple = compose((x -> x*10), (x -> Arrays.asList(new Integer[] { x - 1, x, x + 1 })) ); List<List<Integer>> bigTriples = Transformer.map(timesTenAndTriple, integers); System.out.println(bigTriples); } } The stored lambda expression is applied 29

  30. Composition in action Output: [[-1, 0, 1], [9, 10, 11], [19, 20, 21], [29, 30, 31], [39, 40, 41], [49, 50, 51], [59, 60, 61], [69, 70, 71], [79, 80, 81], [89, 90, 91]] 30

  31. Summary Anonymous classes avoid the need to declare a named class when only a simple activity is required. A functional interface can be implemented in a more light-weight manner: by passing a method reference for the single abstract method. A lambda expression can be used in place of a method reference, leading to very concise code Take home message: none of this lets you do anything you could not already do. It simply allows more elegant and concise code 31

  32. Next time A few more details about anonymous classes A discussion of default methods (a.k.a. defender methods) in Java 8. 32

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#