Не совсем точно, что вы не получите, но объясните примеры:
trait X[A] { def map[B](f: A => B): X[B] }
A trait
подобен интерфейсу Java, который также может иметь конкретные методы.
X
- это имя признака, а [A]
- это параметр типа - подумайте об обобщениях Java <A>
. (Часто A
, B
и т. Д. Используются для типов элементов в коллекциях и T
в других местах, но это просто соглашение.)
Эта черта указывает член с именем map
, который является методом с другим параметром типа [B]
и принимает аргумент функции типа A => B
с типом возврата X[B]
. Здесь все абстрактно, потому что нет тела метода.
Бит, который вы можете упустить, это то, что A => B
- это сокращение от Function1[A, B]
. Function1
- это тип функциональных объектов, принимающих 1 аргумент. (A, B) => C
- это сокращение от Function2[A, B, C]
и т. Д. Вы можете создавать свои собственные Function
типы в Java - это забавное упражнение. Функциональный объект - это, по сути, просто объект, имеющий метод apply
, производящий результат из некоторых аргументов.
(1 to 10) map { x => x * 2 }
К ним относятся нотации без точек, где a.method(b)
записывается как a method b
. Итак, to
- это метод для RichInt
, который принимает Int
и производит Range
. map
- это метод для Range
, принимающий аргумент Function1 (помните, что функция - это просто объект типа Function1
).
=>
также используется для написания самих функций (в дополнение к типу уровня, как описано выше). Таким образом, следующие объекты одинаковы, все объекты типа Int => Int
:
(x: Int) => x + 1
new Function1[Int, Int] { def apply(x: Int) = x + 1 }
// note Function1 is a trait, not a class,
// so this the same as `new Object with Function[Int, Int]`
new (Int => Int) { def apply(x: Int) = x + 1 }
Scala использует вывод типа , так что вам не нужно добавлять все типы самостоятельно, если есть определенный тип функции (или любой другой параметризованный тип), ожидаемый из контекста, например,
val f : Int => Int = x => x + 1
val f : Int => Int = _ + 1
Надеюсь, вы сможете увидеть, что означает это обозначение подчеркивания. Подчеркивание полезно, так как в противном случае всегда будет некоторое повторение, поскольку RHS определения функции должен использовать параметры из LHS. Другим примером может быть функция, отображающая String
в его длину:
val f: String => Int = _.length
Так как типы значений обычно выводятся, вы можете предоставить только необходимую аннотацию типа с помощью
val f = (_: String).length
Вероятно, это немного сбивает с толку, потому что синтаксический вывод на сахар и тип означает, что есть несколько способов написать одну и ту же вещь, но как только вы ее получите, вы обнаружите, что она существует, чтобы облегчить вашу жизнь и уменьшить шум. Хорошо поиграйте с ними в REPL, если вы еще этого не сделали.