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> layoutvertical
We 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?