Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How "Effective Java" influenced Kotlin

B471eb2445c405a8d33f5777816550e3?s=47 Lukas Lechner
September 29, 2017

How "Effective Java" influenced Kotlin

B471eb2445c405a8d33f5777816550e3?s=128

Lukas Lechner

September 29, 2017
Tweet

Transcript

  1. How Effective Java influenced Kotlin Lukas Lechner + = @lukleDev

  2. + =

  3. Item 2: “Consider a builder, when faced with many constructor

    parameters”
  4. public class NutritionFacts { private final int servingSize; // required

    private final int servings; // required private final int calories; optional private final int fat; // optional private final int sodium; // optional private final int carbohydrate; // optional } • Telescoping Constructor Pattern • JavaBeans Pattern Item 2: “Consider a builder, when faced with many constructor parameters”
  5. The Builder Pattern • Client calls constructor with all of

    the required parameters • Gets a builder object • Client calls setter-like method on the builder object • Client calls build() method to generate the immutable object NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100) .sodium(35) .carbohydrate(27) .build();
  6. Default Parameter Values & Named Arguments

  7. class KotlinNutritionFacts( private val servingSize: Int, private val servings: Int,

    private val calories: Int = 0, private val fat: Int = 0, private val sodium: Int = 0, private val carbohydrates: Int = 0) Default Values in Kotlin
  8. Creating an object with named arguments val cocaCola = KotlinNutritionFacts(

    servingSize = 240, servings = 8, calories = 100, sodium = 35, carbohydrates = 27)
  9. Item Summary: • Effective Java recommends to use the builder

    pattern • Boilerplate • Builder Pattern not needed in Kotlin • Default Parameter Values and Named Arguments + =
  10. Bonus Tip: Builder-Style APIs with Kotlin’s apply{ } TextView textView

    = new TextView(context); textView.setId(99); textView.setText("Hello World"); textView.setTextSize(16f); val TextView = TextView(context).apply { id = 99 text = "Hello World" textSize = 16f }
  11. Item 3: “Enforce the singleton property with a private constructor

    or an enum type”
  12. How to create a Singleton in Java public class Elvis

    { private static final Elvis INSTANCE = new Elvis(); private Elvis() { } public static Elvis getInstance() { return INSTANCE; } public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); } }
  13. Object Declarations in Kotlin object KotlinElvis { fun leaveTheBuilding(){ println("Whoa

    baby, I'm outta here!") } }
  14. Item Summary: • Object Declarations in Kotlin give us Singletons

    without applying a pattern + =
  15. Item 4: “Enforce non instantiability with a private constructor”

  16. Utility Classes in Java public class Arrays { // Suppresses

    default constructor, ensuring non-instantiability. private Arrays() {} ... public static void sort(int[] a) { ... } ... }
  17. Top Level Functions in Kotlin // File: Array.kt fun sort(a:

    IntArray) { ... }
  18. Item Summary: • Non instantiability needs to be enforced because

    every function in Java must belong to a class • Kotlin: Top Level functions, no private constructor needed + =
  19. Item 5: “Avoid creating unnecessary objects”

  20. public class Sum { // Hideously slow program! public static

    void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } } Slow Java Program
  21. public class Sum { // Hideously slow program! public static

    void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } } Slow Java Program
  22. public class Sum { // Hideously slow program! public static

    void main(String[] args) { // primitive long instead of the Long reference type long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } } Fast Java Program
  23. Primitive and Reference Types in Java Primitive Types: int, long,

    float, double, byte, … Reference Types: Integer, Long, Float, Double, String, … • Primitives hold values • Primitives hold only functional values • Primitives are more time- and space-efficient
  24. Be cautious about what Type you use in Java •

    Item 5: “Avoid creating unnecessary Objects” • Item 49: “Prefer primitive types to reference types” ◦ Always use primitives unless you are forced to use reference types in ▪ Collections ▪ Type arguments in generic classes
  25. Types in Kotlin • Int • Long • String =>

    Primitive or reference types?
  26. Kotlin Compiler generates objects of primitive or reference types •

    Most of the times, Java primitive types are generated • Exceptions: ◦ objects in collections, ◦ type arguments in generic classes ◦ nullable types! // So we will still have performance issues if we use Long? var sum: Long? = 0L
  27. Item Summary: • We don’t need to care about which

    kind of type we use • Kotlin Compiler makes that decision • Compiler sticks to advice of Effective Java • We cannot make the mistakes you can make in Java, that Effective Java points out like creating unnecessary objects + =
  28. Bonus Tip: Equality in Java and Kotlin • Java ◦

    == ◦ .equals() • Kotlin ◦ == Structural Equality ◦ === Referential Equality
  29. Chapter 3: Methods common to All Objects

  30. Chapter 3: Methods Common to All Objects • Item 8

    “Obey the general contract when overriding equals” • Item 9 “Always override hashCode when you override equals” • Item 10 “Always override toString()” • Item 11 “Override clone judiciously” • More than pages 20 pages
  31. Live Demo

  32. Chapter Summary: • the whole third chapter shows us how

    to create proper immutable value objects in Java • how to properly implement toHashCode(), equals() and clone() • in Kotlin, the compiler is doing that for us • data class + =
  33. Item 14: In public classes, use accessor methods, not public

    fields
  34. public class JavaPerson { // don't use public fields in

    public classes! public String name; public Integer age; } • we loose all the benefits of encapsulation • we can’t add additional logic in accessors
  35. public class JavaPerson { private String name; private Integer age;

    }
  36. public class JavaPerson { private String name; private Integer age;

    public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
  37. Properties instead of Fields

  38. class KotlinPerson { var name: String? = null var age:

    Int? = null }
  39. class KotlinPerson { var name: String? = null var age:

    Int? = null } kotlinPerson.name
  40. class KotlinPerson { var name: String? = null var age:

    Int? = null } kotlinPerson.name kotlinPerson.age = 18
  41. class KotlinPerson { var name: String? = null var age:

    Int? = null set(value) { if (value in 0..120){ field = value } else{ throw IllegalArgumentException() } } }
  42. Item Summary: • in Kotlin we can not make the

    mistake of using public fields • Kotlin uses properties instead of fields as first class language feature + =
  43. Item 16: Favor composition over inheritance

  44. public class InstrumentedHashSet<E> extends HashSet<E> { // The number of

    attempted element insertions private int addCount = 0; public InstrumentedHashSet() {} @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount;} public static void main(String[] args) { InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }
  45. public class ForwardingSet<E> implements Set<E> { private final Set<E> s;

    public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e);} public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c);} public boolean addAll(Collection<? extends E> c) {return s.addAll(c);} public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } ... } Java Forwarding class to enable Composition
  46. public class ForwardingSet<E> implements Set<E> { private final Set<E> s;

    public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e);} public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c);} public boolean addAll(Collection<? extends E> c) {return s.addAll(c);} public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } ... } Java Forwarding class to enable Composition
  47. public class ForwardingSet<E> implements Set<E> { private final Set<E> s;

    public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e);} public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c);} public boolean addAll(Collection<? extends E> c) {return s.addAll(c);} public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } ... } Java Forwarding class to enable Composition
  48. public class InstrumentedSet<E> extends ForwardingSet<E> { private int addCount =

    0; public InstrumentedSet(Set<E> s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedSet<String> s = new InstrumentedSet<String>(new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } } Correctly prints out 3 !
  49. Class Delegation

  50. class InstrumentedSet<E>(val set: MutableSet<E>) : MutableSet<E> by set { var

    addCount = 0 override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } fun main(args: Array<String>) { val s = InstrumentedSet(HashSet<String>()) s.addAll(Arrays.asList("Snap", "Crackle", "Pop")) println(s.addCount) // Correctly prints 3 } Class Delegates in Kotlin
  51. class InstrumentedSet<E>(val set: MutableSet<E>) : MutableSet<E> by set { var

    addCount = 0 override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } fun main(args: Array<String>) { val s = InstrumentedSet(HashSet<String>()) s.addAll(Arrays.asList("Snap", "Crackle", "Pop")) println(s.addCount) // Correctly prints 3 } Class Delegates in Kotlin
  52. class InstrumentedSet<E>(val set: MutableSet<E>) : MutableSet<E> by set { var

    addCount = 0 override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } fun main(args: Array<String>) { val s = InstrumentedSet(HashSet<String>()) s.addAll(Arrays.asList("Snap", "Crackle", "Pop")) println(s.addCount) // Correctly prints 3 } Class Delegates in Kotlin
  53. class InstrumentedSet<E>(val set: MutableSet<E>) : MutableSet<E> by set { var

    addCount = 0 override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } fun main(args: Array<String>) { val s = InstrumentedSet(HashSet<String>()) s.addAll(Arrays.asList("Snap", "Crackle", "Pop")) println(s.addCount) // Correctly prints 3 } Class Delegates in Kotlin
  54. class InstrumentedSet<E>(val set: MutableSet<E>) : MutableSet<E> by set { var

    addCount = 0 override fun add(element: E): Boolean { addCount++ return set.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return set.addAll(elements) } } fun main(args: Array<String>) { val s = InstrumentedSet(HashSet<String>()) s.addAll(Arrays.asList("Snap", "Crackle", "Pop")) println(s.addCount) // Correctly prints 3 } Class Delegates in Kotlin
  55. Item Summary: • Effective Java recommends to favor composition over

    inheritance • shows how to achieve it with forwarding classes • Kotlin supports composition natively with class delegation • requires zero boiler plate code + =
  56. Item 17: Design and document for inheritance or else prohibit

    it
  57. Java • Classes are open by default • Explicit final

    keyword needed to make it final Kotlin • Classes are final by default • Explicit open keyword needed to make it open
  58. Item Summary: • In Kotlin we have to make the

    explicit choice of making a class subclassable with the open keyword + =
  59. Item 22: Favor static member classes over nonstatic

  60. Member Classes in Java public class Outer { private int

    bar = 1; class Nested { public int foo() { return bar; } } } non-static public class Outer { private int bar = 1; static class Nested { public int foo() { return 2; } } } static
  61. Member Classes in Kotlin - static by default class Outer

    { private val bar: Int = 1 class Nested { fun foo() = 2 } } static class Outer { private val bar: Int = 1 inner class Nested { fun foo() = bar } } non static
  62. Item Summary: • Effective Java recommends to favor static member

    classes over non-static • In Kotlin, member classes are static by default + =
  63. Other relevant Chapters & Items • Chapter 5: Generics ◦

    No raw types in Kotlin ◦ Easier way of defining variance • Chapter 6: Enums and Annotations ◦ Consistently use the override annotation
  64. Other relevant Chapters & Items • Chapter 7: Methods ◦

    Check parameters for validity • Chapter 9: Exceptions ◦ Avoid unnecessary use of checked exceptions More Information in my Blogpost Series on Medium: medium.com/@lukleDev
  65. + =

  66. @lukleDev + = Questions?