Определение методов
Синтаксис простого метода выглядит следующим образом:
def methodName(param1: Type1, param2: Type2): ReturnType =
// здесь тело метода
end methodName // опционально, можно не указывать
В этом синтаксисе:
- ключевое слово
def
используется для определения метода - для наименования методов согласно стандартам Scala используется camel case convention
- у параметров метода необходимо всегда указывать тип
- возвращаемый тип метода указывать необязательно
- методы могут состоять как только из одной строки, так и из нескольких строк
- метку окончания метода
end methodName
указывать необязательно, её рекомендуется указывать только для длинных методов
Вот два примера однострочного метода с именем add
, который принимает два входных параметра Int
.
Первая версия явно показывает возвращаемый тип метода - Int
, а вторая - нет:
def add(a: Int, b: Int): Int = a + b
def add(a: Int, b: Int) = a + b
У публичных методов рекомендуется всегда указывать тип возвращаемого значения. Объявление возвращаемого типа может упростить его понимание при просмотре кода другого человека или своего кода спустя некоторое время.
Вызов методов
Вызов методов прост:
val x = add(1, 2)
Коллекции Scala имеют десятки встроенных методов. Эти примеры показывают, как их вызывать:
val x = List(1, 2, 3)
// x: List[Int] = List(1, 2, 3)
x.size
// res0: Int = 3
x.contains(1)
// res1: Boolean = true
x.map(_ * 10)
// res2: List[Int] = List(10, 20, 30)
Внимание:
size
не принимает аргументов и возвращает количество элементов в списке- метод
contains
принимает один аргумент — значение для поиска map
принимает один аргумент - функцию; в данном случае в него передается анонимная функция
Многострочные методы
Если метод длиннее одной строки, начинайте тело метода со второй строки с отступом вправо:
def addThenDouble(a: Int, b: Int): Int =
val sum = a + b
sum * 2
addThenDouble(1, 1)
// res3: Int = 4
В этом методе:
sum
— неизменяемая локальная переменная; к ней нельзя получить доступ вне метода- последняя строка удваивает значение
sum
- именно это значение возвращается из метода
Обратите внимание, что нет необходимости в операторе return
в конце метода.
Поскольку почти все в Scala является выражением — то это означает, что каждая строка кода возвращает
(или вычисляет) значение — нет необходимости использовать return
.
Это видно на примере того же метода, но в более сжатой форме:
def addThenDouble(a: Int, b: Int): Int = (a + b) * 2
В теле метода можно использовать все возможности Scala:
if
/else
выраженияmatch
выражения- циклы
while
- циклы
for
иfor
выражения - присвоение переменных
- вызовы других методов
- определения других методов
В качестве ещё одного примера многострочного метода,
getStackTraceAsString
преобразует свой входной параметр Throwable
в правильно отформатированную строку:
def getStackTraceAsString(t: Throwable): String =
val sw = StringWriter()
t.printStackTrace(PrintWriter(sw))
sw.toString
В этом методе:
- в первой строке переменная
sw
принимает значение нового экземпляраStringWriter
- вторая строка сохраняет содержимое трассировки стека в
StringWriter
- третья строка возвращает строковое представление трассировки стека
Рекомендации о методах, которые не принимают параметров
Когда метод не принимает параметров, говорят, что он имеет arity уровень 0 (arity-0). Аналогично, если метод принимает один параметр - это метод с arity-1.
Когда создаются методы arity-0:
- если метод выполняет побочные эффекты, такие как вызов
println
, метод объявляется с пустыми скобками. - если метод не выполняет побочных эффектов, например, получение размера коллекции, что аналогично доступу к полю в коллекции, круглые скобки опускаются.
Например, этот метод выполняет побочный эффект, поэтому он объявлен с пустыми скобками:
def speak() = println("hi")
При вызове метода нужно обязательно указывать круглые скобки, если он был объявлен с ними:
speak // error: "method speak must be called with () argument"
speak() // prints "hi"
Хотя это всего лишь соглашение, его соблюдение значительно улучшает читаемость кода: с первого взгляда становится понятно, что метод с arity-0 имеет побочные эффекты.
Использование if в качестве тела метода
Поскольку выражения if
/else
возвращают значение, их можно использовать в качестве тела метода.
Вот метод с именем isTruthy
, реализующий Perl-определения true
и false
:
def isTruthy(a: Any) =
if a == 0 || a == "" || a == false then
false
else
true
Примеры показывают, как работает метод:
isTruthy(0)
isTruthy("")
isTruthy("hi")
isTruthy(1.0)
Использование match в качестве тела метода
Довольно часто в качестве тела метода используются match-выражения.
Вот еще одна версия isTruthy
, написанная с match
выражением:
def isTruthy(a: Matchable) = a match
case 0 | "" | false => false
case _ => true
Этот метод работает точно так же, как и предыдущий, в котором использовалось выражение if
/else
.
Вместо Any
в качестве типа параметра используется Matchable
,
чтобы принять любое значение, поддерживающее сопоставление с образцом (pattern matching).
Контроль видимости методов в классах
В классах, объектах, trait
-ах и enum
-ах методы Scala по умолчанию общедоступны,
поэтому созданный здесь экземпляр Dog
может получить доступ к методу speak
:
class Dog:
def speak() = println("Woof")
val d = new Dog
d.speak()
// Woof
Также методы можно помечать как private
.
Это делает их закрытыми в текущем классе, поэтому их нельзя вызвать или переопределить в подклассах:
class Animal:
private def breathe() = println("I’m breathing")
class Cat extends Animal:
// this method won’t compile
override def breathe() = println("Yo, I’m totally breathing")
Если необходимо сделать метод закрытым в текущем классе, но разрешить подклассам вызывать или переопределять его,
метод помечается как protected
, как показано в примере с методом speak
:
class Animal:
private def breathe() = println("I’m breathing")
def walk() =
breathe()
println("I’m walking")
protected def speak() = println("Hello?")
class Cat extends Animal:
override def speak() = println("Meow")
val cat = new Cat
cat.walk()
cat.speak()
cat.breathe() // won’t compile because it’s private
Настройка protected
означает:
- к методу (или полю) могут обращаться другие экземпляры того же класса
- метод (или поле) не виден в текущем пакете
- он доступен для подклассов
Методы в объектах
Ранее было показано, что trait
-ы и классы могут иметь методы.
Ключевое слово object
используется для создания одноэлементного класса, и object
также может содержать методы.
Это хороший способ сгруппировать набор "служебных" методов.
Например, этот объект содержит набор методов, которые работают со строками:
object StringUtils:
def truncate(s: String, length: Int): String = s.take(length)
def lettersAndNumbersOnly_?(s: String): Boolean =
s.matches("[a-zA-Z0-9]+")
def containsWhitespace(s: String): Boolean =
s.matches(".*\\s.*")
end StringUtils
Вызов методов суперкласса
Методы суперкласса можно вызвать через ключевое слово super.
, как показано в примере:
class Animal:
def speak() = println("Hello?")
class Cat extends Animal:
override def speak() =
super.speak()
println("Meow")
val cat = new Cat
cat.speak()
// Hello?
// Meow
Ссылки: