Введение

Существуют разные типы Polymorphism, а именно:

  1. Subtype Polymorphism: Помните OOP? да это оттуда.
  2. Parametric Polymorphism: Дженерики! с параметрами типа. Функция или тип данных будут одинаково обрабатывать значения разных типов.
  3. Ad hoc polymorphism: он также основан на дженериках, поэтому похож на Parametric Polymorphis, но с изюминкой (см. ниже).

Параметрический полиморфизм VS Ad Hoc

Итак, у нас есть 3 типа полиморфизма, не так ли просто полиморфный :) subtype мы знаем из старого доброго OOP. Разница между Parameteric и Ad Hoc интригует. Они оба используют generics, чтобы код мог работать на другом типе. Таким образом, когда у вас есть Parametric Полиморфизм, вы определяете некоторую общую функциональность, и у вас есть type parameter теперь, когда вы запускаете этот код и предоставляете type parameter этот общий код, который будет работать с этим type parameter Однако разница между этим и Ad Hoc Polymorphism заключается в том, что Ad Hoc Polymorphism «исследует» типы ad hoc полиморфизм использует, возможно, другую реализацию в зависимости от типов. Таким образом, в стандарте Parametric Polymorphism один и тот же код работает с разными типами, а в ad hoc polymorphism это будет другой код, основанный на фактическом типе.

Параметрический полиморфизм

Что ж, параметрический полиморфизм, как следует из названия, просто параметрический. Это означает, что мы указываем функцию с общим параметром, затем, когда мы создаем экземпляр этой функции или что-то еще, мы указываем наш текущий тип, и текущий тип будет применен функцией, что не так!

Давайте посмотрим на пример:

object GenericPolyPlayground {
  trait GenericStack[T] {
    var elms: List[T] = Nil
    def push(x: T) { elms = x :: elms }
    def peak(): T = elms.head
    def pop(): T =
    {
      val h = elms.head
      elms = elms.tail
      h
    }
  }
}

Итак, что у нас там есть, у нас есть общая реализация stack, мы не указываем, какого типа этот стек, и он всегда будет выполнять операции над общим списком, таким как head tail, поэтому все, что мы поместим в этот GenericStack, получит помещается на общий List и, таким образом, будет выполнять те же операции. Давайте попробуем.

val intStack = new GenericPolyPlayground.GenericStack[Int] {}
    intStack.push(1)
    println(s"with generic polymorphism it will just poop 1, so boring: ${intStack.pop()}") // it always has the same pop implementation.
    
    // Output:
    
    // with generic polymorphism it will just poop 1, so boring: 1

Итак, вы видите, мы нажали 1, и он напечатал 1, ничего особенного.

Специальный полиморфизм

С помощью специального полиморфизма мы можем фактически change вести поведение универсального кода на основе типа, который мы полиморфируем! давайте посмотрим пример:

object AdHocPolyPlayground {
  trait AdHocStack[T] {
    var elms: List[T] = Nil
    def push(x: T) {
      elms = x :: elms
    }
    def peak(): T = elms.head
    def pop()(implicit crazy: Crazy[T]): T = {
      crazy.omg()
      val h = elms.head
      elms = elms.tail
      h
    }
  }
  trait Crazy[T] {
    def omg()
  }
  implicit val crazyInt: Crazy[Int] = new Crazy[Int] {
    def omg() {
      println("omg i'm crazy")
    }
  }
  implicit val crazyLong: Crazy[Long] = new Crazy[Long] {
    def omg() {println("omg im so long!") }
  }
}

В приведенной выше реализации мы вызываем дополнительную функцию из pop общей реализации, которую мы вызываем: crazy.omg();

и что crazy.omg отличается в зависимости от типа, который вы видите, у нас есть один omg для Int, а другой для Long, поэтому наша общая реализация меняется в зависимости от типа, который мы полиморфируем, см. пример ниже:

val intCrazyStack = new AdHocPolyPlayground.AdHocStack[Int] {}
    intCrazyStack.push(1)
    println(s"with ad hoc polymorphism it will initiate crazy stuff for type int! we change behaviour based on type: ${intCrazyStack.pop()}")
    val longCrazyStack = new AdHocPolyPlayground.AdHocStack[Long] {}
    longCrazyStack.push(1)
    println(s"with ad hoc polymorphism it will initiate a different crazy stuff for long! we change generic behaviour based on type: ${longCrazyStack.pop()}")
  }
  
  // and the output:
    
  omg i'm crazy
  with ad hoc polymorphism it will initiate crazy stuff for type int! we change behaviour based on type: 1
  omg im so long!
  with ad hoc polymorphism it will initiate a different crazy stuff for long! we change generic behaviour based on   type: 1

Резюме

У нас есть 3 типа полиморфизма, подтип, adhoc и общий. Подтип — это стандартный полиморфизм ООП. С помощью generic мы указываем универсальный код для запуска на различных типах, а с помощью специального полиморфизма мы можем изменить общий код, выполняемый на основе фактического полиморфного типа! :)