В Scala есть и функции, и методы. В большинстве случаев мы можем игнорировать различия между ними. Но иногда мы должны понимать их различия.
Методы в Scala похожи на методы в Java; методы являются частью класса. У метода есть имя, сигнатура типа, иногда аннотации к методу и код функциональной реализации (байт-код) метода.
Функции в Scala являются полноценными объектами. Scala использует 22 трейта для абстрагирования понятия функций. Эти 22 трейта варьируются от Function1 до Function22:
Как показано на рисунке выше, Function10 представляет: функцию с 10 формальными параметрами и возвращаемым значением R (ковариантным).
Функции в Scala фактически являются объектами классов, наследующих эти трейты. Например, мы определяем функцию с помощью функционального литерала:
Фактически, приведённое выше определение функции эквивалентно следующему определению:
Поскольку Function2 — это трейт, его нельзя напрямую инстанцировать с помощью new. Приведённый вышеnew Function2[Int,Int,Int](){}фактически определяет и создаёт экземпляр объекта класса, реализующего трейт Function2.
apply — это синтаксический сахар в Scala: при вызове obj() у объекта obj компилятор Scala преобразует его в obj.apply(); при вызове clazz() у класса clazz компилятор Scala преобразует его в clazz_company_obj.apply(), где clazz_company_obj — объект-компаньон clazz.
Конкретные различия суммируются следующим образом:
1. Метод не может существовать как самостоятельное выражение (кроме методов без параметров), а функция может. Например:
В приведённом выше примере мы сначала определили метод m, а затем определили функцию f. Затем мы используем имя функции (значение функции) в качестве итогового выражения. Поскольку сам f является
объектом (объектом, реализующим трейт FunctionN), такое использование совершенно корректно. Но если мы используем имя метода в качестве итогового выражения, это вызовет ошибку.
2. Функция должна иметь список параметров, тогда как метод может не иметь списка параметров.
В приведённом выше примере метод m1 принимает ноль параметров, поэтому список параметров можно опустить. Но функция не может опустить список параметров.
3. Имя метода — это вызов метода, тогда как имя функции представляет только сам объект функции.
Это легче понять. Поскольку переменная, хранящая функциональный литерал (также называемая именем функции или значением функции), сама является объектом класса, реализующего трейт FunctionN. Чтобы вызвать метод apply этого объекта, нужно использовать синтаксис obj(). Поэтому добавление круглых скобок после имени функции — это способ вызвать функцию. Как показано ниже:
4. Там, где требуется функция, если передаётся метод, автоматически выполняется ETA-расширение (преобразование метода в функцию).
Если мы напрямую присвоим метод переменной, будет выдана ошибка. Если указать тип переменной как функцию, то она сможет пройти компиляцию. Вот так:
Конечно, мы также можем принудительно преобразовать метод в функцию, для чего используются частично применённые функции Scala:
5. Параметр по имени по существу является методом.
Параметр по имени по существу представляет собой метод с пустым списком параметров. Вот так:
Приведённый выше код фактически определяет метод m1, а параметр m1 — это параметр по имени (метод). Поскольку для метода без параметров имя метода является вызовом метода , List(x, x) фактически выполняет два вызова метода.
ПосколькуList(x,x)выполняет два вызова метода, получаются два разных значения.
Если мы немного изменим определение m1 и сначала закэшируем x, результат будет сильно отличаться от предыдущего.
Оригинальная статья: https://blog.csdn.net/u012302488/article/details/50543661