Les lambda arrivent!
En attendant, êtes-vous sûr d'avoir
compris les génériques?
Henri Tremblay
Architecte Senior
OCTO Technology

@henri_tremblay
27 au 29 mars 2013
Les lambda arrivent!
En attendant, êtes-vous sûr d'avoir
compris les génériques?
Henri Tremblay
Architecte Senior
OCTO Technology

@henri_tremblay
27 au 29 mars 2013
Henri Tremblay
OCTO Technology
Responsable R&D
Performance
Calcul parallèle

Open Source
EasyMock
Objenesis
September 2013
September 2013
March 2014
March 2014
September 2004
Lambda

return Tweet.TWEETS.stream()
.collect(Collectors
.partitioningBy(
t->t.containsHashTag("#lambda")));
Lambda = Fun with generics
Stream<Tweet> stream = Tweet.TWEETS.stream();
Predicate<Tweet> lambda = t -> t.containsHashTag("#lambda");
Collector<Tweet, ?, Map<Boolean, List<Tweet>>> collector =
Collectors.partitioningBy(lambda);

return stream.collect(collector);
Lambda = Fun with generics
Stream<Tweet> stream = Tweet.TWEETS.stream();
Function<Tweet, Boolean> lambda = t -> t.containsHashTag("#lambda");
Collector<Tweet, Map<Boolean, List<Tweet>>> collector =
Collectors.<Tweet, Boolean, List<Tweet>, Map<Boolean, List<Tweet>>>
groupingBy(lambda, HashMap::new, ArrayList::new);
return stream.collect(collector);
What do I need to know?
Why

© OCTO 2011

12
What do I need to know?
Why

Covariance

© OCTO 2011

13
What do I need to know?
Why

Covariance
Capture

© OCTO 2011

14
What do I need to know?
Why

Covariance
Capture
Inference
© OCTO 2011

15
What do I need to know?
Why

Covariance
Capture
Inference
Erasure
© OCTO 2011

16
What do I need to know?
Why

Covariance

Capture

Erasure
© OCTO 2011

Inference

Bridge
17
Faire compiler

18
Dreaded warnings
Type safety: The expression of type List needs
unchecked conversion to conform to List<String>

Type safety: Unchecked cast from
List<capture#1-of ?> to List<String>
19
Ostrich defense
@SuppressWarnings("unchecked")

20
Why

© OCTO 2011

21

27 au 29 mars 2013
Rule #1

A code compiling
without warning should
never ever cause a
ClassCastException
22
Covariance

23

27 au 29 mars 2013
Arrays
Arrays are covariant:
Number n = Integer.MAX_VALUE;
Number[] list = new Integer[0];
Generics are not:
List<Number> l =

new ArrayList<Integer>(); // Illegal

24
Why not?
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
int i = li.get(0); // ClassCastException

Would work if covariant

And allow to break rule #1

25
Why for array?
Integer[] list = // ...
foo(list);

public void foo(Object[] o) {
// ...
}
Arrays and generics don’t mix well
Can’t have an array of generics
List<String>[] lsa = new List<String>[10];// illegal

27
Because
If it was allowed
List<String>[] lsa = new List<String>[10]; // illegal
Object[] oa = lsa; // OK (covariant)
oa[0] = new ArrayList<Integer>(); // OK
String s = lsa[0].get(0); // bad

28
Exception

List<?>[] l = new ArrayList<?>[3];

29
Capture

30

27 au 29 mars 2013
Capture usually is
Type
List<?> bar();
<T> IExpectationSetters<T> expect(T value);
void andReturn(T value); // Method of IExpectationSetters
expect(bar()).andReturn(new ArrayList<String>());

And you get
The method andReturn(List<capture#6-of ?>) in the type
IExpectationSetters<List<capture#6-of ?>> is not applicable for the arguments
(ArrayList<String>)
31
Detail
List<?> bar();
<T> IExpectationSetters<T> expect(T value);
void andReturn(T value);
expect(bar()).andReturn(new ArrayList<String>());
List<Capture#6> bar = bar();
IExpectationSetters<List<Capture#6>> es =
expect(bar());
es.andReturn(List<Capture#6> value);
32
Only solution
We need to cast
expect((List<String>) bar()).andReturn(new
Tell to expect we want a
ArrayList<String>());
List<String>

But still a warning
Type safety: Unchecked cast from List<capture#6-of ?> to
List<String>
Framework coder tip:
Try to never return a wildcard unless necessary
33
Inference

34
Diamonds are a programmer best friend
List<String> l = new ArrayList<>();
How the compiler tells the type
Determine
the return
value type

The parameter

<T> T anyObject(T clazz)

36
How the compiler tells the type
The assigned
variable

Determine the
return type

MyType var = <T> T anyObject()

37
But watch out with overloading
public void foo(String s)
public void foo(Object o)

foo(anyObject());
Can’t
guess the
type
38
Trick #1
Determine the return
value type

Artificially give the type with a
dedicated parameter

<T> T anyObject(Class<T> clazz)

39
But how to pass a generic?
public void foo(String s)
public void foo(Object o)

foo(anyObject(List<String>.class));

Illegal
40
Some solutions
This would work

foo((String) anyObject());
foo((List<String>) anyObject()); // Warning
But still doesn’t work for
generics
41
Trick #2
So the only solution is
foo(EasyMock.<List<String>> anyObject());

… which sadly doesn’t support static imports
foo(.<List<String>> anyObject()); // Illegal

42
Trick #2 applied to Lambda
Return type: Map<Boolean, List<Tweet>>
return Tweet.TWEETS.stream()
.collect( Collectors.<Tweet, Boolean,
List<Tweet>, Map<Boolean, List<Tweet>>>
groupingBy(t->t.containsHashTag("#lambda"),
HashMap::new, ArrayList::new));
Lambda = Inference
return Tweet.TWEETS.stream()
.collect(Collectors
.partitioningBy(
t->t.containsHashTag("#lambda"));
How did he do it?
List<Tweet> list = Tweet.TWEETS;
Stream<Tweet> stream = list.stream();
R result = stream.collect(Collector<? super T, ?, R> collector);
R result = stream.collect(Collector<? super Tweet, ?, R> collector);
Collector<T, ?, Map<Boolean, List<T>>> collector =
Collectors.partitioningBy(Predicate<? super T> predicate);
Collector<Tweet, ?, Map<Boolean, List<Tweet>>> collector =
Collectors.partitioningBy(Predicate<? super Tweet> predicate);
Map<Boolean, List<Tweet>> result = stream.collect(Collector<? super Tweet, ?,
Map<Boolean, List<Tweet>>> collector);
Predicate<? super Tweet> lambda = (Tweet t) -> t.containsHashTag("#lambda");
Trick #3: Lambda inference

Object o = (Runnable) () -> {
System.out.println("hi");
};

Collections.sort(strings,
(String a, String b) -> a.compareTo(b));
Erasure

47
Erasure…
public void foo() {
List<String> l = new ArrayList<String>();

for (String s : l) {

No type

System.out.println(s);
}

public void foo() {

}

List l = new ArrayList();
for (String s : l) {

System.out.println(s);

Compilation

}
}
… or not erasure
public class A extends ArrayList<String> {}

public static void main(final String[] args) {
ParameterizedType type = (ParameterizedType)
A.class.getGenericSuperclass();
System.out.println(
type.getActualTypeArguments()[0]);
}

 prints class java.lang.String
49
Type class
java.lang.reflect.Type
•
•
•
•
•

GenericArrayType
ParameterizedType
TypeVariable
WildcardType
Implemented by Class

New powers unleashed!

java.lang.reflect.GenericDeclaration
Implemented by Class, Method, Constructor
50
Useful!
class A {}

abstract class BaseDao<T> {
public T load(final long id) {
// …
}
}
class ADao extends BaseDao<A> {}
© OCTO 2011

51
Useful!
@SuppressWarnings("unchecked")
public T load(final long id) {
ParameterizedType type =
(ParameterizedType) getClass()
.getGenericSuperclass();

ADao

BaseDao<A>

A

Type actualType = type.getActualTypeArguments()[0];
return em.find((Class<T>) actualType, (Long) id);
}

Unsafe cast
Bridge

© OCTO 2011

53
Everything seems normal…
class A<T> {
abstract void set(T value);
}
class B extends A<String> {
String value;
@Override
void set(final String value) {
this.value = value;
}
}
But is not
class B extends A {
void set(String value) {
this.value = value;
}
volatile void set(Object o){
set((String)o);
}

}
Example
Raw type warning

Perfectly compiling

A a = new B();
a.set(new Object());
But at runtime:
java.lang.ClassCastException
The actual problem being
B.class.getDeclaredMethods()
This

Returns that

volatile void set(java.lang.Object)
void B.set(java.lang.String)
And gives you no way to find out
which method is bridged
What about lambdas?
public class A {
public static void main(String[] args) {
Method[] methods = A.class.getDeclaredMethods();
Arrays.stream(methods).forEach(m ->
System.out.println(m + " "
+ m.isBridge() + " " + m.isSynthetic()));
}
}
Prints this
public static void A.main(java.lang.String[]) false false

private static void A.lambda$0(java.lang.reflect.Method) false true
Conclusion

Who has learned
something today?

© OCTO 2011

59
Useful links
Nice lambda tutorial (in French):
http://lambda.ninjackaton.ninja-squad.com/

Description of inference types on lambda:
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

Everything on generics:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Conclusion

Questions?

http://perfug.github.io/

+Henri
@henritremblay
htr@octo.com
© OCTO 2011

http://brownbaglunch.fr
61

Lambdas and Generics (long version) - Bordeaux/Toulouse JUG

  • 1.
    Les lambda arrivent! Enattendant, êtes-vous sûr d'avoir compris les génériques? Henri Tremblay Architecte Senior OCTO Technology @henri_tremblay 27 au 29 mars 2013
  • 2.
    Les lambda arrivent! Enattendant, êtes-vous sûr d'avoir compris les génériques? Henri Tremblay Architecte Senior OCTO Technology @henri_tremblay 27 au 29 mars 2013
  • 3.
    Henri Tremblay OCTO Technology ResponsableR&D Performance Calcul parallèle Open Source EasyMock Objenesis
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    Lambda = Funwith generics Stream<Tweet> stream = Tweet.TWEETS.stream(); Predicate<Tweet> lambda = t -> t.containsHashTag("#lambda"); Collector<Tweet, ?, Map<Boolean, List<Tweet>>> collector = Collectors.partitioningBy(lambda); return stream.collect(collector);
  • 11.
    Lambda = Funwith generics Stream<Tweet> stream = Tweet.TWEETS.stream(); Function<Tweet, Boolean> lambda = t -> t.containsHashTag("#lambda"); Collector<Tweet, Map<Boolean, List<Tweet>>> collector = Collectors.<Tweet, Boolean, List<Tweet>, Map<Boolean, List<Tweet>>> groupingBy(lambda, HashMap::new, ArrayList::new); return stream.collect(collector);
  • 12.
    What do Ineed to know? Why © OCTO 2011 12
  • 13.
    What do Ineed to know? Why Covariance © OCTO 2011 13
  • 14.
    What do Ineed to know? Why Covariance Capture © OCTO 2011 14
  • 15.
    What do Ineed to know? Why Covariance Capture Inference © OCTO 2011 15
  • 16.
    What do Ineed to know? Why Covariance Capture Inference Erasure © OCTO 2011 16
  • 17.
    What do Ineed to know? Why Covariance Capture Erasure © OCTO 2011 Inference Bridge 17
  • 18.
  • 19.
    Dreaded warnings Type safety:The expression of type List needs unchecked conversion to conform to List<String> Type safety: Unchecked cast from List<capture#1-of ?> to List<String> 19
  • 20.
  • 21.
    Why © OCTO 2011 21 27au 29 mars 2013
  • 22.
    Rule #1 A codecompiling without warning should never ever cause a ClassCastException 22
  • 23.
  • 24.
    Arrays Arrays are covariant: Numbern = Integer.MAX_VALUE; Number[] list = new Integer[0]; Generics are not: List<Number> l = new ArrayList<Integer>(); // Illegal 24
  • 25.
    Why not? List<Integer> li= new ArrayList<Integer>(); List<Number> ln = li; // illegal ln.add(new Float(3.1415)); int i = li.get(0); // ClassCastException Would work if covariant And allow to break rule #1 25
  • 26.
    Why for array? Integer[]list = // ... foo(list); public void foo(Object[] o) { // ... }
  • 27.
    Arrays and genericsdon’t mix well Can’t have an array of generics List<String>[] lsa = new List<String>[10];// illegal 27
  • 28.
    Because If it wasallowed List<String>[] lsa = new List<String>[10]; // illegal Object[] oa = lsa; // OK (covariant) oa[0] = new ArrayList<Integer>(); // OK String s = lsa[0].get(0); // bad 28
  • 29.
    Exception List<?>[] l =new ArrayList<?>[3]; 29
  • 30.
  • 31.
    Capture usually is Type List<?>bar(); <T> IExpectationSetters<T> expect(T value); void andReturn(T value); // Method of IExpectationSetters expect(bar()).andReturn(new ArrayList<String>()); And you get The method andReturn(List<capture#6-of ?>) in the type IExpectationSetters<List<capture#6-of ?>> is not applicable for the arguments (ArrayList<String>) 31
  • 32.
    Detail List<?> bar(); <T> IExpectationSetters<T>expect(T value); void andReturn(T value); expect(bar()).andReturn(new ArrayList<String>()); List<Capture#6> bar = bar(); IExpectationSetters<List<Capture#6>> es = expect(bar()); es.andReturn(List<Capture#6> value); 32
  • 33.
    Only solution We needto cast expect((List<String>) bar()).andReturn(new Tell to expect we want a ArrayList<String>()); List<String> But still a warning Type safety: Unchecked cast from List<capture#6-of ?> to List<String> Framework coder tip: Try to never return a wildcard unless necessary 33
  • 34.
  • 35.
    Diamonds are aprogrammer best friend List<String> l = new ArrayList<>();
  • 36.
    How the compilertells the type Determine the return value type The parameter <T> T anyObject(T clazz) 36
  • 37.
    How the compilertells the type The assigned variable Determine the return type MyType var = <T> T anyObject() 37
  • 38.
    But watch outwith overloading public void foo(String s) public void foo(Object o) foo(anyObject()); Can’t guess the type 38
  • 39.
    Trick #1 Determine thereturn value type Artificially give the type with a dedicated parameter <T> T anyObject(Class<T> clazz) 39
  • 40.
    But how topass a generic? public void foo(String s) public void foo(Object o) foo(anyObject(List<String>.class)); Illegal 40
  • 41.
    Some solutions This wouldwork foo((String) anyObject()); foo((List<String>) anyObject()); // Warning But still doesn’t work for generics 41
  • 42.
    Trick #2 So theonly solution is foo(EasyMock.<List<String>> anyObject()); … which sadly doesn’t support static imports foo(.<List<String>> anyObject()); // Illegal 42
  • 43.
    Trick #2 appliedto Lambda Return type: Map<Boolean, List<Tweet>> return Tweet.TWEETS.stream() .collect( Collectors.<Tweet, Boolean, List<Tweet>, Map<Boolean, List<Tweet>>> groupingBy(t->t.containsHashTag("#lambda"), HashMap::new, ArrayList::new));
  • 44.
    Lambda = Inference returnTweet.TWEETS.stream() .collect(Collectors .partitioningBy( t->t.containsHashTag("#lambda"));
  • 45.
    How did hedo it? List<Tweet> list = Tweet.TWEETS; Stream<Tweet> stream = list.stream(); R result = stream.collect(Collector<? super T, ?, R> collector); R result = stream.collect(Collector<? super Tweet, ?, R> collector); Collector<T, ?, Map<Boolean, List<T>>> collector = Collectors.partitioningBy(Predicate<? super T> predicate); Collector<Tweet, ?, Map<Boolean, List<Tweet>>> collector = Collectors.partitioningBy(Predicate<? super Tweet> predicate); Map<Boolean, List<Tweet>> result = stream.collect(Collector<? super Tweet, ?, Map<Boolean, List<Tweet>>> collector); Predicate<? super Tweet> lambda = (Tweet t) -> t.containsHashTag("#lambda");
  • 46.
    Trick #3: Lambdainference Object o = (Runnable) () -> { System.out.println("hi"); }; Collections.sort(strings, (String a, String b) -> a.compareTo(b));
  • 47.
  • 48.
    Erasure… public void foo(){ List<String> l = new ArrayList<String>(); for (String s : l) { No type System.out.println(s); } public void foo() { } List l = new ArrayList(); for (String s : l) { System.out.println(s); Compilation } }
  • 49.
    … or noterasure public class A extends ArrayList<String> {} public static void main(final String[] args) { ParameterizedType type = (ParameterizedType) A.class.getGenericSuperclass(); System.out.println( type.getActualTypeArguments()[0]); }  prints class java.lang.String 49
  • 50.
    Type class java.lang.reflect.Type • • • • • GenericArrayType ParameterizedType TypeVariable WildcardType Implemented byClass New powers unleashed! java.lang.reflect.GenericDeclaration Implemented by Class, Method, Constructor 50
  • 51.
    Useful! class A {} abstractclass BaseDao<T> { public T load(final long id) { // … } } class ADao extends BaseDao<A> {} © OCTO 2011 51
  • 52.
    Useful! @SuppressWarnings("unchecked") public T load(finallong id) { ParameterizedType type = (ParameterizedType) getClass() .getGenericSuperclass(); ADao BaseDao<A> A Type actualType = type.getActualTypeArguments()[0]; return em.find((Class<T>) actualType, (Long) id); } Unsafe cast
  • 53.
  • 54.
    Everything seems normal… classA<T> { abstract void set(T value); } class B extends A<String> { String value; @Override void set(final String value) { this.value = value; } }
  • 55.
    But is not classB extends A { void set(String value) { this.value = value; } volatile void set(Object o){ set((String)o); } }
  • 56.
    Example Raw type warning Perfectlycompiling A a = new B(); a.set(new Object()); But at runtime: java.lang.ClassCastException
  • 57.
    The actual problembeing B.class.getDeclaredMethods() This Returns that volatile void set(java.lang.Object) void B.set(java.lang.String) And gives you no way to find out which method is bridged
  • 58.
    What about lambdas? publicclass A { public static void main(String[] args) { Method[] methods = A.class.getDeclaredMethods(); Arrays.stream(methods).forEach(m -> System.out.println(m + " " + m.isBridge() + " " + m.isSynthetic())); } } Prints this public static void A.main(java.lang.String[]) false false private static void A.lambda$0(java.lang.reflect.Method) false true
  • 59.
  • 60.
    Useful links Nice lambdatutorial (in French): http://lambda.ninjackaton.ninja-squad.com/ Description of inference types on lambda: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html Everything on generics: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
  • 61.