Slide 1

Slide 1 text

How Effective Java influenced Kotlin Lukas Lechner + = @lukleDev

Slide 2

Slide 2 text

+ =

Slide 3

Slide 3 text

Item 2: “Consider a builder, when faced with many constructor parameters”

Slide 4

Slide 4 text

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”

Slide 5

Slide 5 text

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();

Slide 6

Slide 6 text

Default Parameter Values & Named Arguments

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Creating an object with named arguments val cocaCola = KotlinNutritionFacts( servingSize = 240, servings = 8, calories = 100, sodium = 35, carbohydrates = 27)

Slide 9

Slide 9 text

Item Summary: ● Effective Java recommends to use the builder pattern ● Boilerplate ● Builder Pattern not needed in Kotlin ● Default Parameter Values and Named Arguments + =

Slide 10

Slide 10 text

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 }

Slide 11

Slide 11 text

Item 3: “Enforce the singleton property with a private constructor or an enum type”

Slide 12

Slide 12 text

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!"); } }

Slide 13

Slide 13 text

Object Declarations in Kotlin object KotlinElvis { fun leaveTheBuilding(){ println("Whoa baby, I'm outta here!") } }

Slide 14

Slide 14 text

Item Summary: ● Object Declarations in Kotlin give us Singletons without applying a pattern + =

Slide 15

Slide 15 text

Item 4: “Enforce non instantiability with a private constructor”

Slide 16

Slide 16 text

Utility Classes in Java public class Arrays { // Suppresses default constructor, ensuring non-instantiability. private Arrays() {} ... public static void sort(int[] a) { ... } ... }

Slide 17

Slide 17 text

Top Level Functions in Kotlin // File: Array.kt fun sort(a: IntArray) { ... }

Slide 18

Slide 18 text

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 + =

Slide 19

Slide 19 text

Item 5: “Avoid creating unnecessary objects”

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Types in Kotlin ● Int ● Long ● String => Primitive or reference types?

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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 + =

Slide 28

Slide 28 text

Bonus Tip: Equality in Java and Kotlin ● Java ○ == ○ .equals() ● Kotlin ○ == Structural Equality ○ === Referential Equality

Slide 29

Slide 29 text

Chapter 3: Methods common to All Objects

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Live Demo

Slide 32

Slide 32 text

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 + =

Slide 33

Slide 33 text

Item 14: In public classes, use accessor methods, not public fields

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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; } }

Slide 37

Slide 37 text

Properties instead of Fields

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

class KotlinPerson { var name: String? = null var age: Int? = null set(value) { if (value in 0..120){ field = value } else{ throw IllegalArgumentException() } } }

Slide 42

Slide 42 text

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 + =

Slide 43

Slide 43 text

Item 16: Favor composition over inheritance

Slide 44

Slide 44 text

public class InstrumentedHashSet extends HashSet { // 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 c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount;} public static void main(String[] args) { InstrumentedHashSet s = new InstrumentedHashSet(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }

Slide 45

Slide 45 text

public class ForwardingSet implements Set { private final Set s; public ForwardingSet(Set 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 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 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

Slide 46

Slide 46 text

public class ForwardingSet implements Set { private final Set s; public ForwardingSet(Set 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 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 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

Slide 47

Slide 47 text

public class ForwardingSet implements Set { private final Set s; public ForwardingSet(Set 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 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 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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Class Delegation

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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 + =

Slide 56

Slide 56 text

Item 17: Design and document for inheritance or else prohibit it

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Item Summary: ● In Kotlin we have to make the explicit choice of making a class subclassable with the open keyword + =

Slide 59

Slide 59 text

Item 22: Favor static member classes over nonstatic

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Item Summary: ● Effective Java recommends to favor static member classes over non-static ● In Kotlin, member classes are static by default + =

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

+ =

Slide 66

Slide 66 text

@lukleDev + = Questions?