Skip to content

Instantly share code, notes, and snippets.

@Jazzatola
Last active August 29, 2015 14:13
Show Gist options
  • Save Jazzatola/1105aabaeeb2c865d40d to your computer and use it in GitHub Desktop.
Save Jazzatola/1105aabaeeb2c865d40d to your computer and use it in GitHub Desktop.
Using covariant and contravariant types to demonstrate the Liskov substitution principle
package com.example
class GrandParent
class Parent extends GrandParent
class Child extends Parent
/*
* contravariant parameter p
* covariant result r
*/
trait Foo[-P, +R] {
def f(p: P): R
}
object Bar {
def g(foo: Foo[Parent, Parent]) = ???
}
class LiskovSubclass extends Foo[GrandParent, Child] {
override def f(p: GrandParent): Child = ???
}
class OtherSubclass extends Foo[Child, GrandParent] {
override def f(p: Child): GrandParent = ???
}
object Main {
def main(args: Array[String]) {
Bar.g(new LiskovSubclass())
Bar.g(new OtherSubclass()) // does not compile
}
}
@tobyweston
Copy link

I've split co/contra variance and tried to convey the pedagogical-point (so to speak) by naming stuff.

class Variance {

  class A {}
  class B extends A {}

  class Container[T] {} // invariant
  class CovariantContainer[+T] {}
  class ContravariantContainer[-T] {}

  def main(args: Array[String]) {
    basicSubTyping
    invariant
    covariant
    contravariant
  }

  private def basicSubTyping {
    var a: A = new A
    val b: B = new B

    a = b
    // b = a // compiler failure
  }

  private def invariant {
    var a = new Container[A]
    var b = new Container[B]

    // a = b  // compiler failure
  }

  private def covariant {
    var a = new CovariantContainer[A]
    var b = new CovariantContainer[B] // sub-type

    a = b
    // b = a  // compiler failure
  }

  private def contravariant {
    var a = new ContravariantContainer[A]
    var b = new ContravariantContainer[B]

    // a = b // compiler failure
    b = a
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment