Вероятно, сейчас уже слишком поздно отвечать, и вы, вероятно, уже знаете разницу, но я собираюсь ответить, чтобы предложить альтернативную точку зрения, поскольку я не уверен, что то, что говорит Грег, правильно. Дженерики являются более общими, чем более высокородные типы. Многие языки, такие как Java и C #, имеют обобщенные типы, но немногие имеют типы более высокого порядка.
Чтобы ответить на ваш конкретный вопрос, да, Box
- это конструктор типа с параметром типа T
. Вы также можете сказать, что это общий тип, хотя это не более высокий родственный тип. Ниже приводится более широкий ответ.
Это определение общего программирования из Википедии:
Обобщенное программирование - это стиль компьютерного программирования, в котором алгоритмы записываются в терминах типов, которые будут определены позже, которые затем при необходимости создаются для конкретных типов, предоставленных в качестве параметров. Этот подход, впервые примененный в ML в 1973 г., 1 позволяет писать общие функции или типы, которые отличаются только набором типов, с которыми они работают при использовании, тем самым уменьшая дублирование.
Допустим, вы определяете Box
вот так. Он содержит элемент определенного типа и имеет несколько специальных методов. Он также определяет функцию map
, что-то вроде Iterable
и Option
, поэтому вы можете взять блок, содержащий целое число, и превратить его в блок, содержащий строку, без потери всех тех специальных методов, которые есть в Box
.
case class Box(elem: Any) {
..some special methods
def map(f: Any => Any): Box = Box(f(elem))
}
val boxedNum: Box = Box(1)
val extractedNum: Int = boxedString.elem.asInstanceOf[Int]
val boxedString: Box = boxedNum.map(_.toString)
val extractedString: String = boxedString.elem.asInstanceOf[String]
Если Box
определен таким образом, ваш код будет действительно некрасивым из-за всех вызовов asInstanceOf
, но, что более важно, он не безопасен для типов, потому что все является Any.
Вот где могут быть полезны дженерики. Допустим, вместо этого мы определяем Box
так:
case class Box[A](elem: A) {
def map[B](f: A => B): Box[B] = Box(f(elem))
}
Затем мы можем использовать нашу map
функцию для любых вещей, например, для изменения объекта внутри Box
, но при этом убедиться, что он находится внутри Box
. Здесь нет необходимости в asInstanceOf
, поскольку компилятор знает тип ваших Box
es и то, что они содержат (даже аннотации типов и аргументы типа не нужны).
val boxedNum: Box[Int] = Box(1)
val extractedNum: Int = boxedNum.elem
val boxedString: Box[String] = boxedNum.map[String](_.toString)
val extractedString: String = boxedString.elem
Обобщения в основном позволяют абстрагироваться от разных типов, позволяя использовать Box[Int]
и Box[String]
как разные типы, даже если вам нужно создать только один Box
класс.
Однако предположим, что у вас нет контроля над этим классом Box
, и он определяется просто как
case class Box[A](elem: A) {
//some special methods, but no map function
}
Допустим, этот API, который вы используете, также определяет свои собственные классы Option
и List
(оба принимают один параметр типа, представляющий тип элементов). Теперь вы хотите иметь возможность отображать все эти типы, но, поскольку вы не можете изменять их самостоятельно, вам придется определить неявный класс, чтобы создать для них метод расширения. Давайте добавим неявный класс Mappable
для метода расширения и класс типов Mapper
.
trait Mapper[C[_]] {
def map[A, B](context: C[A])(f: A => B): C[B]
}
implicit class Mappable[C[_], A](context: C[A])(implicit mapper: Mapper[C]) {
def map[B](f: A => B): C[B] = mapper.map(context)(f)
}
Вы можете определить неявные сопоставители следующим образом
implicit object BoxMapper extends Mapper[Box] {
def map[B](box: Box[A])(f: A => B): Box[B] = Box(f(box.elem))
}
implicit object OptionMapper extends Mapper[Option] {
def map[B](opt: Option[A])(f: A => B): Option[B] = ???
}
implicit object ListMapper extends Mapper[List] {
def map[B](list: List[A])(f: A => B): List[B] = ???
}
//and so on
и используйте его, как если бы Box
, Option
, List
и т. д. всегда имели map
методы.
Здесь Mappable
и Mapper
- это типы более высокого порядка, а Box
, Option
и List
- типы первого порядка. Все они являются универсальными типами и конструкторами типов. Int
и String
, однако, являются правильными типами. Вот их виды (виды относятся к типам, а типы относятся к значениям).
//To check the kind of a type, you can use :kind in the REPL
Kind of Int and String: *
Kind of Box, Option, and List: * -> *
Kind of Mappable and Mapper: (* -> *) -> *
Конструкторы типов в некоторой степени аналогичны функциям (которые иногда называют конструкторами значений). Правильный тип (вид *
) аналогичен простому значению. Это конкретный тип, который вы можете использовать для возвращаемых типов, таких как типы ваших переменных и т. Д. Вы можете просто прямо сказать val x: Int
, не передавая Int
какие-либо параметры типа.
Тип первого порядка (вид * -> *
) подобен функции, которая выглядит как Any => Any
. Вместо того, чтобы принимать значение и давать вам значение, он берет тип и дает вам другой тип. Вы не можете использовать типы первого порядка напрямую (val x: List
не будет работать) без указания им параметров типа (val x: List[Int]
работает). Это то, что делают дженерики - они позволяют абстрагироваться от типов и создавать новые типы (JVM просто стирает эту информацию во время выполнения, но такие языки, как C ++, буквально генерируют новые классы и функции). Параметр типа C
в Mapper
тоже того же типа. Параметр типа подчеркивания (вы также можете использовать что-нибудь еще, например x
) позволяет компилятору знать, что C
относится к типу * -> *
.
Тип высшего порядка / тип высшего порядка подобен функции высшего порядка - он принимает в качестве параметра другой конструктор типа. Вы не можете использовать Mapper[Int]
выше, потому что C
должен иметь вид * -> *
(чтобы вы могли использовать C[A]
и C[B]
), тогда как Int
просто *
. Только в таких языках, как Scala и Haskell с типами более высокого порядка, вы можете создавать типы, подобные Mapper
выше, и другие вещи помимо языков с более ограниченными системами типов, таких как Java.
Этот ответ (и другие) на аналогичный вопрос также может помочь.
Изменить: я украл это очень полезное изображение из того же ответа:
![введите описание изображения здесь](https://i.stack.imgur.com/FXwSC.png)
person
user
schedule
29.06.2020
Box
не тип, ноBox[_]
. - person Chris Martin   schedule 24.06.2014