Введение
Существуют разные типы Polymorphism
, а именно:
Subtype Polymorphism
: ПомнитеOOP
? да это оттуда.Parametric Polymorphism
: Дженерики! с параметрами типа. Функция или тип данных будут одинаково обрабатывать значения разных типов.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 мы указываем универсальный код для запуска на различных типах, а с помощью специального полиморфизма мы можем изменить общий код, выполняемый на основе фактического полиморфного типа! :)