Определение методов

Синтаксис простого метода выглядит следующим образом:

def methodName(param1: Type1, param2: Type2): ReturnType =
  // здесь тело метода
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)

Внимание:

Многострочные методы

Если метод длиннее одной строки, начинайте тело метода со второй строки с отступом вправо:

def addThenDouble(a: Int, b: Int): Int =
  val sum = a + b
  sum * 2
addThenDouble(1, 1)  
// res3: Int = 4

В этом методе:

Обратите внимание, что нет необходимости в операторе return в конце метода. Поскольку почти все в Scala является выражением — то это означает, что каждая строка кода возвращает (или вычисляет) значение — нет необходимости использовать return.

Это видно на примере того же метода, но в более сжатой форме:

def addThenDouble(a: Int, b: Int): Int = (a + b) * 2

В теле метода можно использовать все возможности Scala:

В качестве ещё одного примера многострочного метода, getStackTraceAsString преобразует свой входной параметр Throwable в правильно отформатированную строку:

def getStackTraceAsString(t: Throwable): String =
  val sw = StringWriter()
  t.printStackTrace(PrintWriter(sw))
  sw.toString

В этом методе:

Рекомендации о методах, которые не принимают параметров

Когда метод не принимает параметров, говорят, что он имеет arity уровень 0 (arity-0). Аналогично, если метод принимает один параметр - это метод с arity-1.

Когда создаются методы arity-0:

Например, этот метод выполняет побочный эффект, поэтому он объявлен с пустыми скобками:

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

Ссылки: