Hanneli Tavante
Breandan Considine
@breandan
@hannelita
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
Vertical
Horizontal
Layout
Layout
Vertical
Horizontal
class Layout {
//superclass
}class Vertical
extends Layout {
}class Horizontal
extends Layout {
}
Vertical
Horizontal
Layout
interface Layout {
}class Vertical
implements Layout {
}class Horizontal
implements Layout {
}
Vertical
Horizontal
Layout
interface Orientation {
}class Vertical implements
Orientation {
}class Horizontal implements
Orientation {
}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();
}
}class Layout<T> {
}class Layout<T> {
}class Vertical {
}class Horizontal {
}
Layout
Vertical
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();
}
}Layout<Horizontal> layoutHorizontal =
new Layout<Horizontal>(new Horizontal());layoutHorizontal.rotate();Orientation result=Layout<Vertical> layoutverticalWe want:
(Layout<Vertical>)layoutHorizontal.rotate();=Explicit casting
:(
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) {
}class Layout<out T : Orientation>(val t: T) {
}
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
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()
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()
*/trait Orientation {}
trait Orientation {}
class Vertical extends Orientation {}
class Horizontal extends Orientation {}
trait Orientation {}
class Vertical extends Orientation {}
class Horizontal extends Orientation {}
abstract class Layout[+T <: Orientation] {
def layout: T
}
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
val layout: Layout[Orientation] = new
HorizontalLayout(new Horizontal)Compile error: "Expression doesn't conform to the expected type"
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]
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)
abstract class Layout[+T <: Orientation] {
def layout: T
def rerender(layout: T)
}Method to re-render the actual screen, keeping the layout.
abstract class Layout[+T <: Orientation] {
def layout: T
def rerender(layout: T)
}Compile error: "Covariant type occurs in contravariant position"
abstract class Layout[+T <: Orientation] {
def layout: T
def rerender[T >: Orientation](layout: T)
}Why does the injected argument needs to be contravariant?
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.
interface Orientation {
fun rotate(): Orientation
}
object Vertical : Orientation {
override fun rotate() = Horizontal
}
object Horizontal : Orientation {
override fun rotate() = Vertical
}
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) {
}open class Builder<out T : Layout<*>> {
open fun build(): T = build()
}
open class Builder<out T : Layout<*>> {
open fun build(): T = build()
}
open class Renderer<in T> {
open fun render(t: T) {
println(t)
}
}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
}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
} class Layout {}Questions?