Typing in Java,

Kotlin and Scala

 

Hanneli Tavante

Breandan Considine

Hi!

@breandan

@hannelita

Disclaimer

We will work on a single example

and rebuild it several times

It is not a language war!

We are going to discuss

some architecture concepts

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

Mobile

Vertical

Horizontal

Layout

How can we represent that in Java?

 

Layout

 

 

Vertical

 

 

Horizontal

 

class Layout {
    //superclass
}
class Vertical 
   extends Layout {

}
class Horizontal 
   extends Layout {

}

 

Vertical

 

 

Horizontal

 

 

Layout

 

Inheritance... meh

  • Tight coupling
  • Reusing base class for unrelated child

Can we come up with a better layout?

interface Layout {

}
class Vertical 
   implements Layout {

}
class Horizontal 
   implements Layout {

}

 

Vertical

 

 

Horizontal

 

 

Layout

 

Does this object have these skills?

Does this object understand this protocol ?

Name refactor

interface Orientation {

}
class Vertical implements
      Orientation {

}
class Horizontal implements
    Orientation {

}

It is time to add some methods

rotate()

rotate() applied to a Horizontal -> Vertical

rotate() applied to a Vertical -> Horizontal

Where should we place the method rotate() ?

interface Orientation {

}
class Vertical implements
      Orientation {

}
class Horizontal implements
    Orientation {

}
interface Orientation {

}
class Vertical implements
      Orientation {

    Horizontal rotate() {
        return 
         new Horizontal();
    }

}
class Horizontal implements
    Orientation {

    Vertical rotate() {
        return 
         new Vertical();
    }

}
interface Orientation {
   Orientation rotate();

}
class Vertical implements
      Orientation {

    Horizontal rotate() {
        return 
         new Horizontal();
    }

}
class Horizontal implements
    Orientation {

    Vertical rotate() {
        return 
         new Vertical();
    }

}

Can we make it better?

We also could use Generics

class Layout<T> {

}
class Layout<T> {

}
class Vertical {

}
class Horizontal {

}

 

Layout

 

 

Vertical

 

 

Horizontal

 

How do we restrict a Layout either Layout<Vertical> or Layout<Horizontal>

interface Orientation {

}
class Vertical implements
      Orientation {

}
class Horizontal implements
    Orientation {

}
class Layout<T extends Orientation> {

}

Can we have a generic Layout rotate() ? 

interface Orientation {
  Orientation rotate();
}
class Vertical implements
      Orientation {

    Horizontal rotate() {
        return new Vertical();
    }
}
class Horizontal implements
    Orientation {

    Vertical rotate() {
        return new Horizontal();
    }
}
class Layout<T extends Orientation> {
  public Layout(T t){
    this.t = t;
  }
  Orientation rotate(){
    t.rotate();
  }
}

We want to achieve:

Layout<Horizontal> layoutHorizontal = 
           new Layout<Horizontal>(new Horizontal());
layoutHorizontal.rotate();
Orientation result
=
Layout<Vertical> layoutvertical

We want:

(Layout<Vertical>)
layoutHorizontal.rotate();
=

Explicit casting

:(

Is there any way to avoid that?

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

In Kotlin:

interface Orientation {
    fun rotate(): Orientation
}
class Vertical : Orientation {
  override 
      fun rotate() = Horizontal()
}
class Horizontal : Orientation {
    override 
      fun rotate() = Vertical()
}
class Layout<out T : Orientation>(val t: T) {
}

In Kotlin:

class Layout<out T : Orientation>(val t: T) {
}

In Kotlin:

class Layout<out T : Orientation>(val t: T) {
}

@JvmName("rotateVertical")
fun Layout<Horizontal>.rotate(): Vertical = this.t.rotate()

@JvmName("rotateHorizontal")
fun Layout<Vertical>.rotate(): Horizontal = this.t.rotate()

Extension functions

In Kotlin:

val horizontal = Layout(Horizontal())
val vertical = Layout(Vertical())

// Type safe rotation!
var v: Layout<Vertical> = horizontal.rotate()
var h: Layout<Horizontal> = vertical.rotate()
h = horizontal.rotate().rotate()
v = vertical.rotate().rotate()

In Kotlin:

val horizontal = Layout(Horizontal())
val vertical = Layout(Vertical())

// Type safe rotation!
var v: Layout<Vertical> = horizontal.rotate()
var h: Layout<Horizontal> = vertical.rotate()
h = horizontal.rotate().rotate()
v = vertical.rotate().rotate()

/* Does not compile!
v = horizontal.rotate().rotate()
h = vertical.rotate().rotate()
*/

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

Redesign In Scala:

trait Orientation {}

Redesign In Scala:

trait Orientation {}

class Vertical extends Orientation {}

class Horizontal extends Orientation {}


Redesign In Scala:

trait Orientation {}

class Vertical extends Orientation {}

class Horizontal extends Orientation {}

abstract class Layout[+T <: Orientation] {
  def layout: T
}


Redesign In Scala:

trait Orientation {}

class Vertical extends Orientation {}

class Horizontal extends Orientation {}

abstract class Layout[T <: Orientation] {
  def layout: T
}

class VerticalLayout(v: Vertical) 
              extends Layout[Vertical] {
  override def layout = v
}

class HorizontalLayout(h: Horizontal) 
               extends Layout[Horizontal] {
  override def layout = h
}

 

Orientation

 

 

Vertical

 

 

Horizontal

 

 

Orientation

 

 

Vertical

 

 

Horizontal

 

Layout

 

Horizontal

 

Layout

 

Vertical

 

 

Orientation

 

LayoutVertical

LayoutHorizontal

In Scala

val layout: Layout[Orientation] = new 
                             HorizontalLayout(new Horizontal)

Compile error: "Expression doesn't conform to the expected type"

In Scala

val layout: Layout[Orientation] = new 
                             HorizontalLayout(new Horizontal)
abstract class Layout[T <: Orientation] {
  def layout: T
}

We need to tell the compiler that

Layout[Horizontal] is a subtype Layout[Orientation]

In Scala

val layout: Layout[Orientation] = new 
                             HorizontalLayout(new Horizontal)
abstract class Layout[+T <: Orientation] {
  def layout: T
}

We need to tell the compiler that

Layout[Horizontal] is a subtype Layout[Orientation]

(Covariance)

In Scala

abstract class Layout[+T <: Orientation] {
  def layout: T

  def rerender(layout: T)
}

Method to re-render the actual screen, keeping the layout.

In Scala

abstract class Layout[+T <: Orientation] {
  def layout: T

  def rerender(layout: T)
}

Compile error: "Covariant type occurs in contravariant position"

In Scala

abstract class Layout[+T <: Orientation] {
  def layout: T

  def rerender[T >: Orientation](layout: T)

}

Why does the injected argument needs to be contravariant?

In Scala

abstract class Layout[+T <: Orientation] {
  def layout: T

  def rerender(layout: T)

}
var layoutWrapper: Layout[Orientation] = 
            new HorizontalLayout(new Horizontal)
  layoutWrapper.rerender(new Vertical)

^That is not right.

That is why input arguments are contravariant.

Scala and Kotlin help you actively think about covariance and contravariance

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

Back to Kotlin

interface Orientation {
    fun rotate(): Orientation
}

object Vertical : Orientation {
    override fun rotate() = Horizontal
}

object Horizontal : Orientation {
    override fun rotate() = Vertical
}

Back to Kotlin

interface Orientation {
    fun rotate(): Orientation
}

object Vertical : Orientation {
    override fun rotate() = Horizontal
}

object Horizontal : Orientation {
    override fun rotate() = Vertical
}

class Layout<out T : Orientation>(val t: T) {
}

Back to Kotlin

open class Builder<out T : Layout<*>> {
    open fun build(): T = build()
}

Back to Kotlin

open class Builder<out T : Layout<*>> {
    open fun build(): T = build()
}

open class Renderer<in T> {
    open fun render(t: T) {
        println(t)
    }
}

Back to Kotlin

open class Builder<out T : Layout<*>> {
    open fun build(): T = build()
}

open class Renderer<in T> {
    open fun render(t: T) {
        println(t)
    }
}

fun covariance(horizontalBuilder: Builder<Layout<Horizontal>>,
               layoutBuilder: Builder<Layout<Orientation>>) {
    // Layout<Horizontal> is a subtype of Layout<Orientation> ✓
    val bldr: Builder<Layout<Orientation>> = horizontalBuilder
    
    // Layout<Orientation> is a supertype of Layout<Horizontal> ✗
    val hlbr: Builder<Layout<Horizontal>> = layoutBuilder //ERROR
}

Back to Kotlin

open class Builder<out T : Layout<*>> {
    open fun build(): T = build()
}

open class Renderer<in T> {
    open fun render(t: T) {
        println(t)
    }
}

fun contrav(layoutRenderer: Renderer<Layout<Orientation>>,
            horizontalRenderer: Renderer<Layout<Horizontal>>) {
    // Layout<Orientation> is a supertype of Layout<Horizontal> ✓
    val hlrd: Renderer<Layout<Horizontal>> = layoutRenderer
    
    // Layout<Horizontal> is a subtype of Layout<Orientation> ✗
    val lrdr: Renderer<Layout<Orientation>> = horizontalRenderer
}

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

JVM Languages

will loose the T parameter!

     class Layout {}

(type erasure)

Strategies

  • TypeTags and Shapeless libraries in Scala
  • Extension functions in Kotlin
  • Reified generics in Kotlin
  • Reflection
  • Manually store T in a field

Agenda

  • Representing Layouts in Java
  • Moving to Kotlin
  • Covariance and contravariance in Scala
  • Covariance and contravariance in Kotlin
  • ByteCode and type erasure
  • Why is this important?

To sum up

  • Think about different coding approaches
  • Different architectures
  • New concepts
  • Understand the goods and the bad parts of a language and when to use each of them

References

Thank you :)

Questions?

 

Typing in Java, Kotlin and Scala - Devoxx US 2017

By Hanneli Tavante (hannelita)

Typing in Java, Kotlin and Scala - Devoxx US 2017

  • 3,659