prev | next | title | layout |
---|---|---|---|
basics.textile |
collections.textile |
Основы языка. Продолжение |
post |
В этом уроке вы узнаете:
- Метод apply
- Объекты
- Функции, тоже являются Объектами
- Пакеты
- Сопоставление с образцом
- Case классы
- try-catch-finally
Метод apply – это синтаксический сахар, который применяется для класса или объекта с единственной целью.
object FooMaker { def apply() = new Foo } scala> class Bar { | def apply() = 0 | } defined class Bar scala> val bar = new Bar bar: Bar = Bar@47711479 scala> bar() res8: Int = 0
Здесь наш экземпляр объекта выглядит так, будто мы просто вызываем метод, но это не так. Подробнее об этом позже!
Объекты используются для хранения одного экземпляра класса. Чаще всего они используются с фабриками объектов.
object Timer { var count = 0 def currentCount(): Long = { count += 1 count } }
Как можно это использовать?
scala> Timer.currentCount() res0: Long = 1
Классы и Объекты могут иметь похожие имена. В этом случае Объект называется ‘Объект-спутник’(Companion Object). Чаще всего мы будем использовать ОБъекты-спутники с Фабриками объектов.
Далее пре��ставлен простой пример, который показывает, как можно использовать Объект-спутник, для того чтобы исключить необходимость в использовании ключевого слова ‘new’ для создания экземпляра объекта.
class Bar(foo: String) object Bar { def apply(foo: String) = new Bar(foo) }
В Scala, мы часто говорим об объектно-функциональном стиле. Что это значит? Чем на самом деле является Функция?
Функция – это набор трейтов. В частности, функция, которая принимает один аргумент является экземпляром трейта Function1. Этот трейт определяет метод apply()
, который является синтаксическим сахаром, о нем мы узнали ранее, он позволяет вам вызывать объект, словно он является функцией.
scala> object addOne extends Function1[Int, Int] { | def apply(m: Int): Int = m + 1 | } defined module addOne scala> addOne(1) res2: Int = 2
Существует функция с 22 аргументами. Почему с 22? Это произвольное магическое число. Я никогда не нуждался в функции с более чем 22 аргументами.
Синтаксический сахар метода apply объединяет двойственность объектного и функционального стилей программирования. Вы можете передавать классы и использовать их в качестве функций, кроме этого функции могут быть просто экземплярами классов.
Означает ли это, что каждый раз, когда вы определяете метод в своем классе, вы фактически получаете экземпляр Function*? Нет, методы в классах – это просто методы. Методы-одиночки, определенные в REPL будут экземплярами Function*.
Классы могут расширять Function* и в этих случаях они могут быть вызваны при помощи ().
scala> class AddOne extends Function1[Int, Int] { | def apply(m: Int): Int = m + 1 | } defined class AddOne scala> val plusOne = new AddOne() plusOne: AddOne = <function1> scala> plusOne(1) res0: Int = 2
Запись extends Function1[Int, Int]
мы можем переписать, используя extends (Int => Int)
class AddOne extends (Int => Int) { def apply(m: Int): Int = m + 1 }
Вы можете организовывать ваш код, используя пакеты.
package com.twitter.example
В верхней части файла объявляется все, что будет в этом пакете.
Значения и функции не могут быть объявлены за пределами класса или объекта. Объекты представляют собой полезный инструмент для организации статических функций.
package com.twitter.example object colorHolder { val BLUE = "Blue" val RED = "Red" }
После этого у вас есть доступ к членам пакета напрямую
println("the color is: " + com.twitter.example.colorHolder.BLUE)
Обратите внимание, что Scala REPL говорит вам когда вы объявляе��е объект:
scala> object colorHolder { | val Blue = "Blue" | val Red = "Red" | } defined module colorHolder
Это дает вам небольшую подсказку, которую разработчики Scala используют для проектирования объектов, которые станут частью модульной системы Scala.
Одна из самых часто используемых возможностей Scala.
Сопоставление значений
val times = 1 times match { case 1 => "one" case 2 => "two" case _ => "some other number" }
Сопоставление с использованием условий
times match { case i if i == 1 => "one" case i if i == 2 => "two" case _ => "some other number" }
Заметьте, как мы пытаемся поймать значение переменной ‘i’.
Используемый знак _
в последнем утверждении – это спецсимвол, он
гарантирует, что мы сможем отловить любое значение. В противном случае вы можете получить
ошибку времени выполнения, если вы попадаете в утверждение, которого не существует. Мы обсудим
это чуть позже.
Смотрите также: В Effective Scala описывеатся когда использовать соспоставление с образцом и правила форматирования сос��оставления с образцом. В “Туре по Scala” также описывается Сопоставление с образцом
Вы можете использовать match
, чтобы управлять значениями типов различными способами.
def bigger(o: Any): Any = { o match { case i: Int if i < 0 => i - 1 case i: Int => i + 1 case d: Double if d < 0.0 => d - 0.1 case d: Double => d + 0.1 case text: String => text + "s" } }
Вспомните про наш калькулятор, который мы рассматривали ранее.
Давайте проведем классификацию по типам.
def calcType(calc: Calculator) = calc match { case calc.brand == "hp" && calc.model == "20B" => "financial" case calc.brand == "hp" && calc.model == "48G" => "scientific" case calc.brand == "hp" && calc.model == "30B" => "business" case _ => "unknown" }
Ничего себе, как-то все слишком сложно. К счастью, Scala предоставляет некоторые полезные инструменты специально для этого случая.
Case классы используются для удобного хранения и поиска соответствий по содержимому класса. Вы можете создавать их без использования ‘new’.
scala> case class Calculator(brand: String, model: String) defined class Calculator scala> val hp20b = Calculator("hp", "20b") hp20b: Calculator = Calculator(hp,20b)
У case классов есть метод ToString, работающий автоматически, и который опирается на аргументы конструктора.
scala> val hp20b = Calculator("hp", "20b") hp20b: Calculator = Calculator(hp,20b) scala> val hp20B = Calculator("hp", "20b") hp20B: Calculator = Calculator(hp,20b) scala> hp20b == hp20B res6: Boolean = true
case классы могут иметь методы, как и обычные классы.
case классы предназначены для использования вместе с сопоставлением с образцом. Давайте упростим наш классификатор из примера с калькулятором.
val hp20b = Calculator("hp", "20B") val hp30b = Calculator("hp", "30B") def calcType(calc: Calculator) = calc match { case Calculator("hp", "20B") => "financial" case Calculator("hp", "48G") => "scientific" case Calculator("hp", "30B") => "business" case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel) }
А это другой способ для последнего сопоставления
case Calculator(_, _) => "Calculator of unknown type"
мы можем не объявлять, что это Calculator совсем.
case _ => "Calculator of unknown type"
Или мы можем связать найденное значение с другим именем
case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
Исключения доступны в Scala при использовании синтаксиса try-catch-finally, который исопользует сопоставление с образцом.
try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") } finally { remoteCalculatorService.close() }
try
то��е ориентирован на выражения
val result: Int = try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => { log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") 0 } } finally { remoteCalculatorService.close() }
Этот код не является примером прекрасного стиля программирования, а просто пример того, как try-catch-finally вычисляет выражения, подобно всему остальному в Scala.
Finally будет вызван после того, как исключение будет обработано.