Ce diaporama a bien été signalé.

Java 8, lambdas, generics: How to survive? - NYC Java Meetup Group

4

Partager

Chargement dans…3
×
1 sur 61
1 sur 61

Java 8, lambdas, generics: How to survive? - NYC Java Meetup Group

4

Partager

Description

Lambdas are sexy. But they are adding complexity. Mixed with generics, it creates a dangerous cocktail. Together, we will start from the bottom of generics and go straight through lambda inference. To explain why it is the way it is and tell you how to survive.

Transcription

  1. 1. 27 au 29 mars 2013 Java 8, Lambdas, Generics: How to survive? Henri Tremblay Java Champion tremblay.pro inc. @henri_tremblay http://blog.tremblay.pro NYC Java Meetup Group
  2. 2. Loves IT Strategy Performance Productivity Great food! Do Open Source Henri Tremblay Try to be useful
  3. 3. September 2004
  4. 4. March 18 2014
  5. 5. Java 7 End of Life
  6. 6. Java 8 support
  7. 7. Lambda return Tweet.TWEETS.stream() .collect(Collectors .partitioningBy( t->t.containsHashTag("#lambda")));
  8. 8. 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);
  9. 9. What do I need to know? 9© OCTO 2011 Why
  10. 10. What do I need to know? 10© OCTO 2011 Why Covariance
  11. 11. What do I need to know? 11© OCTO 2011 Why Covariance Capture
  12. 12. What do I need to know? 12© OCTO 2011 Why Covariance Capture Inference
  13. 13. What do I need to know? 13© OCTO 2011 Why Covariance Capture Inference Erasure
  14. 14. What do I need to know? 14© OCTO 2011 Why Covariance Capture Inference Erasure Bridge
  15. 15. Compiled successfully! 15
  16. 16. Dreaded warnings 16 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>
  17. 17. Ostrich defense @SuppressWarnings("unchecked") 17
  18. 18. 27 au 29 mars 2013 Why 18© OCTO 2011
  19. 19. Rule #1 A code compiling without warning should never ever cause a ClassCastException 19
  20. 20. 27 au 29 mars 2013 Covariance 20
  21. 21. Arrays Arrays are covariant: Number n = Integer.MAX_VALUE; Number[] list = new Integer[0]; Generics are not: List<Number> l = new ArrayList<Integer>(); // Illegal 21
  22. 22. 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 22 Would work if covariant And allow to break rule #1
  23. 23. Why for array? Integer[] list = // ... foo(list); public void foo(Object[] o) { // ... }
  24. 24. Arrays and generics don’t mix well Can’t have an array of generics List<String>[] lsa = new List<String>[10];// illegal 24
  25. 25. Exception List<?>[] l = new ArrayList<?>[3]; 25
  26. 26. Because If it was allowed List<String>[] lsa = new List<String>[10]; // illegal Object[] oa = lsa; // OK (covariant) oa[0] = new ArrayList<Integer>(); // OK oa[0].add(42); String s = lsa[0].get(0); // bad 26
  27. 27. 27 au 29 mars 2013 Capture 27
  28. 28. 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>) 28
  29. 29. 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); 29
  30. 30. Only solution We need to cast expect((List<String>) bar()).andReturn(new ArrayList<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 30 Tell to expect we want a List<String>
  31. 31. Inference 31
  32. 32. Diamonds are a programmer best friend List<String> l = new ArrayList<>();
  33. 33. How the compiler tells the type <T> T anyObject(T clazz) 33 The parameterDetermine the return value type
  34. 34. How the compiler tells the type MyType var = <T> T anyObject() 34 Determine the return type The assigned variable
  35. 35. But watch out with overloading public void foo(String s) public void foo(Object o) foo(anyObject()); 35 Can’t guess the type
  36. 36. Trick #1 <T> T anyObject(Class<T> clazz) 36 Artificially give the type with a dedicated parameter Determine the return value type
  37. 37. But how to pass a generic? public void foo(String s) public void foo(Object o) foo(anyObject(List<String>.class)); 37 Illegal
  38. 38. Some solutions foo((String) anyObject()); foo((List<String>) anyObject()); // Warning 38 This would work But still doesn’t work for generics
  39. 39. Trick #2 So the only solution is foo(EasyMock.<List<String>> anyObject()); … which sadly doesn’t support static imports foo(.<List<String>> anyObject()); // Illegal 39
  40. 40. Trick #2 applied to Lambda return Tweet.TWEETS.stream() .collect( Collectors.<Tweet, Boolean, List<Tweet>, Map<Boolean, List<Tweet>>> groupingBy(t->t.containsHashTag("#lambda"), HashMap::new, ArrayList::new)); Return type: Map<Boolean, List<Tweet>>
  41. 41. Lambda = Inference return Tweet.TWEETS.stream() .collect(Collectors .partitioningBy( t->t.containsHashTag("#lambda"));
  42. 42. How did it do it? Tweet.TWEETS.stream() List<Tweet> list = Tweet.TWEETS; Stream<Tweet> stream = list.stream();
  43. 43. How did it do it? Stream<Tweet> stream = list.stream(); R result = stream.collect(Collector<? super T, ?, R> collector); R result = stream.collect(Collector<? super Tweet, ?, R> collector);
  44. 44. How did it do it? 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);
  45. 45. How did it do it? Predicate<? super Tweet> lambda = t -> t.containsHashTag("#lambda"); We now know that So the best t can be is a Tweet Predicate<? super Tweet> lambda = (Tweet t) -> t.containsHashTag("#lambda");
  46. 46. Trick #3: Lambda inference Object o = (Runnable) () -> { System.out.println("hi"); }; Collections.sort(strings, (String a, String b) -> a.compareTo(b));
  47. 47. Erasure 47
  48. 48. Erasure… public void foo() { List<String> l = new ArrayList<String>(); for (String s : l) { System.out.println(s); } } No type public void foo() { List l = new ArrayList(); for (String s : l) { System.out.println(s); } } Compilation
  49. 49. … 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
  50. 50. Type class java.lang.reflect.Type • GenericArrayType • ParameterizedType • TypeVariable • WildcardType • Implemented by Class java.lang.reflect.GenericDeclaration Implemented by Class, Method, Constructor 50 New powers unleashed!
  51. 51. Useful! class A {} abstract class BaseDao<T> { public T load(final long id) { // … } } class ADao extends BaseDao<A> {} 51© OCTO 2011
  52. 52. Useful! @SuppressWarnings("unchecked") public T load(final long id) { ParameterizedType type = (ParameterizedType) getClass() .getGenericSuperclass(); Type actualType = type.getActualTypeArguments()[0]; return em.find((Class<T>) actualType, (Long) id); } ADao A BaseDao<A> Unsafe cast
  53. 53. Bridge 53© OCTO 2011
  54. 54. 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; } }
  55. 55. But is not class B extends A { void set(String value) { this.value = value; } volatile void set(Object o){ set((String)o); } }
  56. 56. Example A a = new B(); a.set(new Object()); But at runtime: java.lang.ClassCastException Raw type warning Perfectly compiling
  57. 57. The actual problem being B.class.getDeclaredMethods() volatile void set(java.lang.Object) void B.set(java.lang.String) This Returns that And gives you no way to find out which method is bridged
  58. 58. 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
  59. 59. Conclusion Who has learned something today? 59© OCTO 2011
  60. 60. 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 Hopefully everything on lambdas http://www.lambdafaq.org/
  61. 61. Conclusion Questions? 61© OCTO 2011 http://www.montreal- jug.org/ http://brownbaglunch.fr @henri_tremblay http://blog.tremblay.pro henri@tremblay.pro

Notes de l'éditeur

  • Hors le fait que ça nous donne des magnifiques oneliners, on y croise une quantité particulièrement épique de génériques
  • Malheureusement, quand on le sépare en petits morceaux, nous voyons apparaître des tonnes de génériques.

    La bonne nouvelle, c’est que ça s’est amélioré. La dernière fois que j’ai donné cette conférence, ça ressemblait plutôt à ça
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Emplit de désespoir vous ne savez pas trop comment vous en débarasser parce que toutes vos tentatives ne compilent pas.

  • Emplit de désespoir vous ne savez pas trop comment vous en débarasser parce que toutes vos tentatives ne compilent pas.

  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Par exemple, les arrays
  • C’est cette règle qui a forcé plusieurs décisions de design et qui a par conséquent rendu notre ami le @SuppressWarning si fréquent.

    On en vient à ce demander comment ils auraient fait si les annotations n’étaient pas apparu au même moment.

  • Par exemple, les arrays
  • Les arrays sont covariants. Si on peut assigner un object d’un type donné à un autre type, alors on peut assigner un array d’objet à un array de l’autre type

    Par contre pour les génériques on ne peut pas. Il est illégal de faire la ligne du bas.

    Car, un code qui compile sans warning ne doit jamais jamais causé un ClassCastException
  • Un exemple que vous avez sûrement déjà vu pour expliquer pourquoi c’est interdit.

    Comme vous voyez, si la covariance était permisse nous briserions la règle #1

    Et pour un array
  • Parce que pour un tableau, on a envie que ça ça passe
  • On ne peut pas faire de tableau de génériques et c’est bien dommage.

    Une seule exception, un tableau de wildcard. Le wildcard étant un type quelconque, nous n’avons pas de soucis.

    Dans tous les autres cas c’est interdit pour la raison suivante
  • On ne peut pas faire de tableau de génériques et c’est bien dommage.

    Une seule exception, un tableau de wildcard. Le wildcard étant un type quelconque, nous n’avons pas de soucis.

    Dans tous les autres cas c’est interdit pour la raison suivante
  • Nous avons le cas suivant.

    Grâce une asticieuse utilisation de la covariance des arrays, si les arrays génériques étaient permis, nous briserions la règle numéro 1.

    En résumé, si les designers des génériques avaient laissé tomber la règle pour ce cas et laisser le bénéfice du doute au développeur, nous aurions échappé à plusieurs complexité.

    Je vous laisse juge de leur décision.
  • Par exemple, les arrays
  • Une méthode retourne un type en wildcard

    Pour garder la cohérence, le compilateur va fixer le type et l’appeler capture#6 pour s’assurer de la cohérence du type donné à T

    Pour les développeurs de framework, ne jamais retourner de wildcard si possible

  • Une méthode retourne un type en wildcard

    Pour garder la cohérence, le compilateur va fixer le type et l’appeler capture#6 pour s’assurer de la cohérence du type donné à T

    Pour les développeurs de framework, ne jamais retourner de wildcard si possible

  • Warning mandatory pour ne pas enfreindre la règle #1

  • Par exemple, les arrays
  • Lorsqu’il y a un paramètre, le compilateur prend le type du paramètre pour déterminer le type de retour.

    Cette pour cette raison que le syntaxe que vous voyez est apparu en Java 5. Le paramètre ne sert à rien sauf à fixer le type.

    Mais ça vous les savez sûrement déjà.

  • Ensuite s’il n’y a pas de paramètre, le compilateur déduit le type à partir de la variable à laquelle elle est assignée.

    Mais ça ne marche pas à tous les coups.
  • Dans les cas d’overloading par exemple. Impossible de deviner quelle méthode doit être appelé
  • Un truc classique est d’ajouter un paramètre juste pour la bonne cause. C’est un contournement classique.
  • Dans les cas d’overloading par exemple. Impossible de deviner quelle méthode doit être appelé
  • Pour les lambda, il est très fort probable d’en avoir besoin.

    Vous avez ici l’exemple du début où je l’utilisais. Fort heureusement, ce n’est pas nécessaire dans la nouvelle API.

    Par contre, j’ai un peu de difficulté à vous promettre que ce sera toujours le cas. On touche du bois
  • Par exemple, les arrays
  • Everybody knows what is erasure?

    List<String> and ArrayList<String> got erasure

    The variable in the loop kept is type

    Boring, nous on va parler des cas où il n’y a pas d’erasure.
  • Oh dear, no erasure


    On récupère la classe parent qui s’avère être générique et implémente donc l’interface ParameterizedType.

  • Il y a eu un ajout de plusieurs interfaces.

    Elles sont passées presque complètement inaperçu mais peuvent être utile.

    Par exemple, l’exemple précédent peut servir à faire ça

  • Par polymorphisme, le getClass() retourne ADao. Donc la superclass est BaseDao, et le type du paramètre générique est A.

    And yes, the dumb part if the SuppressWarnings. It comes come the (Class<T>) cast. Since the compiler can’t be sure that actualType is a Class, we get the warning. On ne peut pas s’en débarasser.
  • Par exemple, les arrays
  • Vous avez une classe générique étendue par un autre qui fixe le type à String. Rien de plus classique.
  • Magie, à la décompilation, une nouvelle méthode est apparue. C’est le bridge. Elle ne sert à rien sauf à l’assurer c’est bien une String qui est passé en paramètre.

    Le volatile c’est parce que la modificateur de champs et de méthodes ont des codes communs. Donc en fait, le décompilateur (jad) a un petit bogue.

    Attention, Java Decompiler ne vous montrera pas le bridge, il le fait disparaitre. On ne le voit qu’avec Jad qui est moins futé.
  • La règle #1 n’est pas enfreinte car nous avons un warning.

    Par contre, Java ajoute dans ce cas particulier une sur-protection.

    Impossible de s’en prémunir à la compilation. Tout est valide. Il fallait donc combler la faille pour empêcher un Object d’être assigné à une String et amener le chaos et l’anarchie dans la JVM. D’où, le bridge.
  • Vous avez deux méthodes. L’une déléguant directement à l’autre.


    Si vous avez un framework faisant des proxies dynamiques ou de l’AOP, vous vous retrouvez avec deux méthodes instrumentées. Et c’est assez rare que c’est ce que vous aviez prévus.

    Résultat, plus rien ne marche. Par exemple, Spring qui utilise beaucoup de proxies, a dû implémenter une class nommée BridgeMethodResolver qui permet de trouver la méthode concrête appelée par le bridge. Et malheureusement, le JDK n’a pas prévu le coup donc il faut la déduire.

    C’est ce que j’avais de plus compliqué à vous montrer aujourd’hui. Passons donc à la conclusion.
  • J’ai pas mal pataugé sur ces sujets car ils sont assez peu documentés. C’est pas simple mais on finit par s’y retrouver.

    La question qui me taraude maintenant c’est “Est-ce que vous connaissiez tout ça?” Pour m’assurez que je ne raconte pas que des banalités.

    Est-ce que chaque personne pourrait lever la main et me dire si vous avez appris au moins une chose aujourd’hui?
  • Maurice Naftalin
  • Oh dear, no erasure


  • Description

    Lambdas are sexy. But they are adding complexity. Mixed with generics, it creates a dangerous cocktail. Together, we will start from the bottom of generics and go straight through lambda inference. To explain why it is the way it is and tell you how to survive.

    Transcription

    1. 1. 27 au 29 mars 2013 Java 8, Lambdas, Generics: How to survive? Henri Tremblay Java Champion tremblay.pro inc. @henri_tremblay http://blog.tremblay.pro NYC Java Meetup Group
    2. 2. Loves IT Strategy Performance Productivity Great food! Do Open Source Henri Tremblay Try to be useful
    3. 3. September 2004
    4. 4. March 18 2014
    5. 5. Java 7 End of Life
    6. 6. Java 8 support
    7. 7. Lambda return Tweet.TWEETS.stream() .collect(Collectors .partitioningBy( t->t.containsHashTag("#lambda")));
    8. 8. 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);
    9. 9. What do I need to know? 9© OCTO 2011 Why
    10. 10. What do I need to know? 10© OCTO 2011 Why Covariance
    11. 11. What do I need to know? 11© OCTO 2011 Why Covariance Capture
    12. 12. What do I need to know? 12© OCTO 2011 Why Covariance Capture Inference
    13. 13. What do I need to know? 13© OCTO 2011 Why Covariance Capture Inference Erasure
    14. 14. What do I need to know? 14© OCTO 2011 Why Covariance Capture Inference Erasure Bridge
    15. 15. Compiled successfully! 15
    16. 16. Dreaded warnings 16 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>
    17. 17. Ostrich defense @SuppressWarnings("unchecked") 17
    18. 18. 27 au 29 mars 2013 Why 18© OCTO 2011
    19. 19. Rule #1 A code compiling without warning should never ever cause a ClassCastException 19
    20. 20. 27 au 29 mars 2013 Covariance 20
    21. 21. Arrays Arrays are covariant: Number n = Integer.MAX_VALUE; Number[] list = new Integer[0]; Generics are not: List<Number> l = new ArrayList<Integer>(); // Illegal 21
    22. 22. 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 22 Would work if covariant And allow to break rule #1
    23. 23. Why for array? Integer[] list = // ... foo(list); public void foo(Object[] o) { // ... }
    24. 24. Arrays and generics don’t mix well Can’t have an array of generics List<String>[] lsa = new List<String>[10];// illegal 24
    25. 25. Exception List<?>[] l = new ArrayList<?>[3]; 25
    26. 26. Because If it was allowed List<String>[] lsa = new List<String>[10]; // illegal Object[] oa = lsa; // OK (covariant) oa[0] = new ArrayList<Integer>(); // OK oa[0].add(42); String s = lsa[0].get(0); // bad 26
    27. 27. 27 au 29 mars 2013 Capture 27
    28. 28. 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>) 28
    29. 29. 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); 29
    30. 30. Only solution We need to cast expect((List<String>) bar()).andReturn(new ArrayList<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 30 Tell to expect we want a List<String>
    31. 31. Inference 31
    32. 32. Diamonds are a programmer best friend List<String> l = new ArrayList<>();
    33. 33. How the compiler tells the type <T> T anyObject(T clazz) 33 The parameterDetermine the return value type
    34. 34. How the compiler tells the type MyType var = <T> T anyObject() 34 Determine the return type The assigned variable
    35. 35. But watch out with overloading public void foo(String s) public void foo(Object o) foo(anyObject()); 35 Can’t guess the type
    36. 36. Trick #1 <T> T anyObject(Class<T> clazz) 36 Artificially give the type with a dedicated parameter Determine the return value type
    37. 37. But how to pass a generic? public void foo(String s) public void foo(Object o) foo(anyObject(List<String>.class)); 37 Illegal
    38. 38. Some solutions foo((String) anyObject()); foo((List<String>) anyObject()); // Warning 38 This would work But still doesn’t work for generics
    39. 39. Trick #2 So the only solution is foo(EasyMock.<List<String>> anyObject()); … which sadly doesn’t support static imports foo(.<List<String>> anyObject()); // Illegal 39
    40. 40. Trick #2 applied to Lambda return Tweet.TWEETS.stream() .collect( Collectors.<Tweet, Boolean, List<Tweet>, Map<Boolean, List<Tweet>>> groupingBy(t->t.containsHashTag("#lambda"), HashMap::new, ArrayList::new)); Return type: Map<Boolean, List<Tweet>>
    41. 41. Lambda = Inference return Tweet.TWEETS.stream() .collect(Collectors .partitioningBy( t->t.containsHashTag("#lambda"));
    42. 42. How did it do it? Tweet.TWEETS.stream() List<Tweet> list = Tweet.TWEETS; Stream<Tweet> stream = list.stream();
    43. 43. How did it do it? Stream<Tweet> stream = list.stream(); R result = stream.collect(Collector<? super T, ?, R> collector); R result = stream.collect(Collector<? super Tweet, ?, R> collector);
    44. 44. How did it do it? 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);
    45. 45. How did it do it? Predicate<? super Tweet> lambda = t -> t.containsHashTag("#lambda"); We now know that So the best t can be is a Tweet Predicate<? super Tweet> lambda = (Tweet t) -> t.containsHashTag("#lambda");
    46. 46. Trick #3: Lambda inference Object o = (Runnable) () -> { System.out.println("hi"); }; Collections.sort(strings, (String a, String b) -> a.compareTo(b));
    47. 47. Erasure 47
    48. 48. Erasure… public void foo() { List<String> l = new ArrayList<String>(); for (String s : l) { System.out.println(s); } } No type public void foo() { List l = new ArrayList(); for (String s : l) { System.out.println(s); } } Compilation
    49. 49. … 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
    50. 50. Type class java.lang.reflect.Type • GenericArrayType • ParameterizedType • TypeVariable • WildcardType • Implemented by Class java.lang.reflect.GenericDeclaration Implemented by Class, Method, Constructor 50 New powers unleashed!
    51. 51. Useful! class A {} abstract class BaseDao<T> { public T load(final long id) { // … } } class ADao extends BaseDao<A> {} 51© OCTO 2011
    52. 52. Useful! @SuppressWarnings("unchecked") public T load(final long id) { ParameterizedType type = (ParameterizedType) getClass() .getGenericSuperclass(); Type actualType = type.getActualTypeArguments()[0]; return em.find((Class<T>) actualType, (Long) id); } ADao A BaseDao<A> Unsafe cast
    53. 53. Bridge 53© OCTO 2011
    54. 54. 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; } }
    55. 55. But is not class B extends A { void set(String value) { this.value = value; } volatile void set(Object o){ set((String)o); } }
    56. 56. Example A a = new B(); a.set(new Object()); But at runtime: java.lang.ClassCastException Raw type warning Perfectly compiling
    57. 57. The actual problem being B.class.getDeclaredMethods() volatile void set(java.lang.Object) void B.set(java.lang.String) This Returns that And gives you no way to find out which method is bridged
    58. 58. 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
    59. 59. Conclusion Who has learned something today? 59© OCTO 2011
    60. 60. 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 Hopefully everything on lambdas http://www.lambdafaq.org/
    61. 61. Conclusion Questions? 61© OCTO 2011 http://www.montreal- jug.org/ http://brownbaglunch.fr @henri_tremblay http://blog.tremblay.pro henri@tremblay.pro

    Notes de l'éditeur

  • Hors le fait que ça nous donne des magnifiques oneliners, on y croise une quantité particulièrement épique de génériques
  • Malheureusement, quand on le sépare en petits morceaux, nous voyons apparaître des tonnes de génériques.

    La bonne nouvelle, c’est que ça s’est amélioré. La dernière fois que j’ai donné cette conférence, ça ressemblait plutôt à ça
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Emplit de désespoir vous ne savez pas trop comment vous en débarasser parce que toutes vos tentatives ne compilent pas.

  • Emplit de désespoir vous ne savez pas trop comment vous en débarasser parce que toutes vos tentatives ne compilent pas.

  • Et vous terminez, pas trop fier de vous et pas trop sûr d’avoir compris le problème avec un aborable SuppressWarning.

    Cette session a pour but de vous faire comprendre
    1- Pourquoi vous avez ces erreurs
    2- Pourquoi c’est fait comme ça
    3- Comment c’est implémenté
  • Par exemple, les arrays
  • C’est cette règle qui a forcé plusieurs décisions de design et qui a par conséquent rendu notre ami le @SuppressWarning si fréquent.

    On en vient à ce demander comment ils auraient fait si les annotations n’étaient pas apparu au même moment.

  • Par exemple, les arrays
  • Les arrays sont covariants. Si on peut assigner un object d’un type donné à un autre type, alors on peut assigner un array d’objet à un array de l’autre type

    Par contre pour les génériques on ne peut pas. Il est illégal de faire la ligne du bas.

    Car, un code qui compile sans warning ne doit jamais jamais causé un ClassCastException
  • Un exemple que vous avez sûrement déjà vu pour expliquer pourquoi c’est interdit.

    Comme vous voyez, si la covariance était permisse nous briserions la règle #1

    Et pour un array
  • Parce que pour un tableau, on a envie que ça ça passe
  • On ne peut pas faire de tableau de génériques et c’est bien dommage.

    Une seule exception, un tableau de wildcard. Le wildcard étant un type quelconque, nous n’avons pas de soucis.

    Dans tous les autres cas c’est interdit pour la raison suivante
  • On ne peut pas faire de tableau de génériques et c’est bien dommage.

    Une seule exception, un tableau de wildcard. Le wildcard étant un type quelconque, nous n’avons pas de soucis.

    Dans tous les autres cas c’est interdit pour la raison suivante
  • Nous avons le cas suivant.

    Grâce une asticieuse utilisation de la covariance des arrays, si les arrays génériques étaient permis, nous briserions la règle numéro 1.

    En résumé, si les designers des génériques avaient laissé tomber la règle pour ce cas et laisser le bénéfice du doute au développeur, nous aurions échappé à plusieurs complexité.

    Je vous laisse juge de leur décision.
  • Par exemple, les arrays
  • Une méthode retourne un type en wildcard

    Pour garder la cohérence, le compilateur va fixer le type et l’appeler capture#6 pour s’assurer de la cohérence du type donné à T

    Pour les développeurs de framework, ne jamais retourner de wildcard si possible

  • Une méthode retourne un type en wildcard

    Pour garder la cohérence, le compilateur va fixer le type et l’appeler capture#6 pour s’assurer de la cohérence du type donné à T

    Pour les développeurs de framework, ne jamais retourner de wildcard si possible

  • Warning mandatory pour ne pas enfreindre la règle #1

  • Par exemple, les arrays
  • Lorsqu’il y a un paramètre, le compilateur prend le type du paramètre pour déterminer le type de retour.

    Cette pour cette raison que le syntaxe que vous voyez est apparu en Java 5. Le paramètre ne sert à rien sauf à fixer le type.

    Mais ça vous les savez sûrement déjà.

  • Ensuite s’il n’y a pas de paramètre, le compilateur déduit le type à partir de la variable à laquelle elle est assignée.

    Mais ça ne marche pas à tous les coups.
  • Dans les cas d’overloading par exemple. Impossible de deviner quelle méthode doit être appelé
  • Un truc classique est d’ajouter un paramètre juste pour la bonne cause. C’est un contournement classique.
  • Dans les cas d’overloading par exemple. Impossible de deviner quelle méthode doit être appelé
  • Pour les lambda, il est très fort probable d’en avoir besoin.

    Vous avez ici l’exemple du début où je l’utilisais. Fort heureusement, ce n’est pas nécessaire dans la nouvelle API.

    Par contre, j’ai un peu de difficulté à vous promettre que ce sera toujours le cas. On touche du bois
  • Par exemple, les arrays
  • Everybody knows what is erasure?

    List<String> and ArrayList<String> got erasure

    The variable in the loop kept is type

    Boring, nous on va parler des cas où il n’y a pas d’erasure.
  • Oh dear, no erasure


    On récupère la classe parent qui s’avère être générique et implémente donc l’interface ParameterizedType.

  • Il y a eu un ajout de plusieurs interfaces.

    Elles sont passées presque complètement inaperçu mais peuvent être utile.

    Par exemple, l’exemple précédent peut servir à faire ça

  • Par polymorphisme, le getClass() retourne ADao. Donc la superclass est BaseDao, et le type du paramètre générique est A.

    And yes, the dumb part if the SuppressWarnings. It comes come the (Class<T>) cast. Since the compiler can’t be sure that actualType is a Class, we get the warning. On ne peut pas s’en débarasser.
  • Par exemple, les arrays
  • Vous avez une classe générique étendue par un autre qui fixe le type à String. Rien de plus classique.
  • Magie, à la décompilation, une nouvelle méthode est apparue. C’est le bridge. Elle ne sert à rien sauf à l’assurer c’est bien une String qui est passé en paramètre.

    Le volatile c’est parce que la modificateur de champs et de méthodes ont des codes communs. Donc en fait, le décompilateur (jad) a un petit bogue.

    Attention, Java Decompiler ne vous montrera pas le bridge, il le fait disparaitre. On ne le voit qu’avec Jad qui est moins futé.
  • La règle #1 n’est pas enfreinte car nous avons un warning.

    Par contre, Java ajoute dans ce cas particulier une sur-protection.

    Impossible de s’en prémunir à la compilation. Tout est valide. Il fallait donc combler la faille pour empêcher un Object d’être assigné à une String et amener le chaos et l’anarchie dans la JVM. D’où, le bridge.
  • Vous avez deux méthodes. L’une déléguant directement à l’autre.


    Si vous avez un framework faisant des proxies dynamiques ou de l’AOP, vous vous retrouvez avec deux méthodes instrumentées. Et c’est assez rare que c’est ce que vous aviez prévus.

    Résultat, plus rien ne marche. Par exemple, Spring qui utilise beaucoup de proxies, a dû implémenter une class nommée BridgeMethodResolver qui permet de trouver la méthode concrête appelée par le bridge. Et malheureusement, le JDK n’a pas prévu le coup donc il faut la déduire.

    C’est ce que j’avais de plus compliqué à vous montrer aujourd’hui. Passons donc à la conclusion.
  • J’ai pas mal pataugé sur ces sujets car ils sont assez peu documentés. C’est pas simple mais on finit par s’y retrouver.

    La question qui me taraude maintenant c’est “Est-ce que vous connaissiez tout ça?” Pour m’assurez que je ne raconte pas que des banalités.

    Est-ce que chaque personne pourrait lever la main et me dire si vous avez appris au moins une chose aujourd’hui?
  • Maurice Naftalin
  • Oh dear, no erasure


  • Plus De Contenu Connexe

    Livres associés

    Gratuit avec un essai de 30 jours de Scribd

    Tout voir

    ×