Джим довольно подробно рассказал об этом в своем блоге , но я публикую здесь брифинг для справки.
Для начала посмотрим, что нам скажет спецификация Scala. Глава 3 (типы) рассказывает нам о типах функций (3.2.9) и типах методов (3.3.1). В главе 4 (основные декларации) говорится о декларации и определениях значений (4.1), декларации и определениях переменных (4.2) и объявлениях и определениях функций (4.6). Глава 6 (выражения) говорит о анонимных функциях (6.23) и значениях методов (6.7). Любопытно, что о значениях функций говорится один раз в 3.2.9, и нигде больше.
A Тип функции - это (примерно) тип формы (T1, ..., Tn) => U , что является сокращением для черты FunctionN
в стандартной библиотеке. Анонимные функции и Значения метода имеют типы функций, и типы функций могут использоваться как часть объявлений и определений значений, переменных и функций. Фактически, он может быть частью типа метода.
A Тип метода является ненулевым типом . Это означает, что нет значение - без объекта, без экземпляра - с типом метода. Как упоминалось выше, Значение метода на самом деле имеет Тип функции . Тип метода - это объявление def
- все о def
, кроме его тела.
Объявления и определения значений и Объявления и определения переменных являются val
и var
объявлениями, включая как тип, так и значение - которые могут быть, соответственно Тип функции и Анонимные функции или значения методов . Обратите внимание, что в JVM эти (значения методов) реализованы с помощью того, что Java называет «методами».
A Объявление функции - это объявление def
, включая type и body . Часть type является типом метода, а тело является выражением или блоком . Это также реализовано в JVM с помощью того, что Java называет «методами».
Наконец, Анонимная функция является экземпляром Типа функции (т. Е. Экземпляром признака FunctionN
) и Значение метода это то же самое! Различие заключается в том, что значение метода создается из методов либо путем добавления нижнего подчеркивания (m _
- это значение метода, соответствующее «объявлению функции» (def
) m
), либо с помощью процесса, называемого Эта-расширение , которое похоже на автоматическое приведение метода к функции.
Так говорят спецификации, поэтому позвольте мне сказать об этом заранее: мы не используем эту терминологию! Это приводит к слишком большой путанице между так называемым "объявлением функции" , которая является частью программы (глава 4 - основные объявления) и «анонимная функция» , которая является выражением, и «тип функции» , которая является, ну вид - черта характера.
Приведенная ниже терминология, используемая опытными программистами Scala, вносит одно изменение в терминологию спецификации: вместо объявления функции , мы говорим method . Или даже объявление метода. Кроме того, мы отмечаем, что объявления значений и объявления переменных также являются методами для практических целей.
Итак, учитывая вышеуказанное изменение терминологии, вот практическое объяснение различия.
A function - это объект, который включает в себя одну из FunctionX
черт, таких как Function0
, Function1
, Function2
и т. Д. Также может включать в себя PartialFunction
, что на самом деле распространяется Function1
.
Давайте посмотрим тип подписи для одной из этих черт:
trait Function2[-T1, -T2, +R] extends AnyRef
У этой черты есть один абстрактный метод (также есть несколько конкретных методов):
def apply(v1: T1, v2: T2): R
И это говорит нам все, что нужно знать об этом. Функция имеет метод apply
, который получает N параметров типов T1 , T2 , ..., TN и возвращает что-то типа R
. Он противоречит вариантам, которые он получает, и ко-вариантам - результат.
Эта разница означает, что Function1[Seq[T], String]
является подтипом Function1[List[T], AnyRef]
. Подтип означает, что его можно использовать вместо it. Легко видеть, что если я собираюсь позвонить f(List(1, 2, 3))
и ожидать AnyRef
назад, то подойдет любой из двух приведенных выше типов.
Теперь, каково сходство метода и функции? Хорошо, если f
является функцией, а m
является методом, локальным для области действия, то оба могут быть вызваны следующим образом:
val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))
Эти вызовы на самом деле разные, потому что первый - просто синтаксический сахар. Scala расширяет его до:
val o1 = f.apply(List(1, 2, 3))
Что, конечно, является вызовом метода для объекта f
. У функций также есть другие синтаксические сахара в качестве преимущества: функциональные литералы (фактически два) и сигнатуры типа (T1, T2) => R
. Например:
val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
case i: Int => "Int"
case d: Double => "Double"
case o => "Other"
}
Другое сходство между методом и функцией заключается в том, что первое можно легко преобразовать во второе:
val f = m _
Scala будет расширяться , что , предполагая, что m
type равен (List[Int])AnyRef
в (Scala 2.7):
val f = new AnyRef with Function1[List[Int], AnyRef] {
def apply(x$1: List[Int]) = this.m(x$1)
}
В Scala 2.8 фактически используется класс AbstractFunction1
для уменьшения размеров классов.
Обратите внимание, что нельзя преобразовать наоборот - из функции в метод.
Методы, однако, имеют одно большое преимущество (ну, два - они могут быть немного быстрее): они могут получать параметры типа . Например, в то время как f
выше может обязательно указывать тип List
, который он получает (List[Int]
в примере), m
может параметризовать его:
def m[T](l: List[T]): String = l mkString ""
Я думаю, что это в значительной степени охватывает все, но я буду рад дополнить это ответами на любые вопросы, которые могут остаться.