Extends scala что это
В этом уроке вы узнаете:
- Метод apply
- Объекты
- Функции, тоже являются Объектами
- Пакеты
- Сопоставление с образцом
- Case классы
- try-catch-finally
Метод apply
Метод 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
Функции, тоже являются Объектами
В 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 = scala> plusOne(1) res0: Int = 2
Запись extends Function1[Int, Int] мы можем переписать, используя extends (Int => Int)
class AddOne extends (Int => Int)
Пакеты
Вы можете организовывать ваш код, используя пакеты.
package com.twitter.example
В верхней части файла объявляется все, что будет в этом пакете.
Значения и функции не могут быть объявлены за пределами класса или объекта. Объекты представляют собой полезный инструмент для организации статических функций.
package com.twitter.example object colorHolder
После этого у вас есть доступ к членам пакета напрямую
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’.
Используемый знак _ в последнем утверждении – это спецсимвол, он
гарантирует, что мы сможем отловить любое значение. В противном случае вы можете получить
ошибку времени выполнения, если вы попадаете в утверждение, которого не существует. Мы обсудим
это чуть позже.
Сопоставление типов
Вы можете использовать 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 Классы
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 Классы и сопоставление с образцом
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
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
Этот код не является примером прекрасного стиля программирования, а просто пример того, как try-catch-finally вычисляет выражения, подобно всему остальному в Scala.
Finally будет вызван после того, как исключение будет обработано.
Built at @twitter by @stevej, @marius, and @lahosken with much help from @evanm, @sprsquish, @kevino, @zuercher, @timtrueman, @wickman, @mccv and @garciparedes; Russian translation by appigram; Chinese simple translation by jasonqu; Korean translation by enshahar;
Extends scala что это
В этом уроке вы узнаете:
- Видимое ограничение («Классы-типы»)
- Типы высшего порядка и специальный полиморфизм
- F-ограниченный полиморфизм / рекурсивные типы
- Структурные типы
- Абстрактные типы членов
- Тип чисток и манифесты
- Пример: Finagle
Видимое ограничение («Классы-типы»)
Неявные функции в Scala позволяют использовать функции по требованию, когда это может помочь при выводе типа, например:
scala> implicit def strToInt(x: String) = x.toInt strToInt: (x: String)Int scala> "123" res0: java.lang.String = 123 scala> val y: Int = "123" y: Int = 123 scala> math.max("123", 111) res1: Int = 123
Видимое ограничение, подобно ограничению типа, требует функцию, которая существует для данного типа, например:
scala> class Container[A defined class Container
Это говорит, что A должен быть «видим» подобно Int. Давайте попробуем.
scala> (new Container[String]).addIt("123") res11: Int = 246 scala> (new Container[Int]).addIt(123) res12: Int = 246 scala> (new Container[Float]).addIt(123.2F) :8: error: could not find implicit value for evidence parameter of type (Float) => Int (new Container[Float]).addIt(123.2) ^
Другие ограничения типов
Методы могут запросить конкретные «доказательства» для типа, а именно:
| A =:= B | A должен быть равен B |
| A | A должен быть подтипом B |
| A | A должен выглядеть как B |
scala> class Container[A](value: A) < def addIt(implicit evidence: A =:= Int) = 123 + value >defined class Container scala> (new Container(123)).addIt res11: Int = 246 scala> (new Container("123")).addIt :10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int]
Кроме того, учитывая наше предыдущее неявное значение, мы можем ослабить ограничение для видимости:
scala> class Container[A](value: A) < def addIt(implicit evidence: A defined class Container scala> (new Container("123")).addIt res15: Int = 246
Обобщенное программирование с помощью видов
В стандартной библиотеке Scala, виды в основном используются для реализации обобщенных функций коллекций. Например, функция «min» (Seq[]), использует эту технику:
def min[B >: A](implicit cmp: Ordering[B]): A = < if (isEmpty) throw new UnsupportedOperationException("empty.min") reduceLeft((x, y) =>if (cmp.lteq(x, y)) x else y) >
Основными преимуществами этого являются:
- Элементам коллекции не требуется реализовывать Ordered, хотя Ordered по-прежнему использует статическую проверку типов.
- Вы можете определить свой собственный порядок сортировки без необходимости использовать дополнительную библиотеку:
scala> List(1,2,3,4).min res0: Int = 1 scala> List(1,2,3,4).min(new Ordering[Int] < def compare(a: Int, b: Int) = b compare a >) res3: Int = 4
Небольшое замечание, есть виды в стандартной библиотеке, которые переводят Ordered в Ordering (и наоборот).
trait LowPriorityOrderingImplicits < implicit def ordered[A >
Ограничения контекста и implicitly[]
В Scala 2.8 введена сокращенная форма для передачи и для доступа с использованием неявных аргументов.
scala> def foo[A](implicit x: Ordered[A]) <> foo: [A](implicit x: Ordered[A])Unit scala> def foo[A : Ordered] <> foo: [A](implicit evidence$1: Ordered[A])Unit
Неявные значения могут быть доступны через implicitly
scala> implicitly[Ordering[Int]] res37: Ordering[Int] = scala.math.Ordering$Int$@3a9291cf
В совокупности это часто приводит к меньшему количеству кода, особенно при передаче с использованием видов.
Типы высшего порядка и специальный полиморфизм
Scala может абстрагировать типы «высшего порядка». Это похоже на каррирование функции. Например, в то время как «унарные типы» имеют конструкторы вроде этого:
List[A]
То есть мы должны удовлетворять определенному «уровню» типовых переменных с целью получения конкретных типов (подобно тому, как uncurried функция должна применяться только к одному списку аргументов, при вызове), типам высшего порядка требуется больше:
scala> trait Container[M[_]] < def put[A](x: A): M[A]; def get[A](m: M[A]): A >scala> val container = new Container[List] < def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head >container: java.lang.Object with Container[List] = $anon$1@7c8e3f75 scala> container.put("hey") res24: List[java.lang.String] = List(hey) scala> container.put(123) res25: List[Int] = List(123)
Заметьте, что Container является полиморфным в параметрическом типе («тип контейнер»).
Если мы объединим использование контейнеров с неявными выражениями, мы получим «специальный» полиморфизм: возможность писать обобщенные контейнеры поверх контейнеров.
scala> trait Container[M[_]] < def put[A](x: A): M[A]; def get[A](m: M[A]): A >scala> implicit val listContainer = new Container[List] < def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head >scala> implicit val optionContainer = new Container[Some] < def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get >scala> def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = < | val c = implicitly[Container[M]] | c.put(c.get(fst), c.get(snd)) | >tupleize: [M[_],A,B](fst: M[A],snd: M[B])(implicit evidence$1: Container[M])M[(A, B)] scala> tupleize(Some(1), Some(2)) res33: Some[(Int, Int)] = Some((1,2)) scala> tupleize(List(1), List(2)) res34: List[(Int, Int)] = List((1,2))
F-ограниченный полиморфизм
Часто необходим доступ к конкретному подклассу в (обобщенном) трейте. Например, представьте себе некоторый трейт, который является обобщенным, но может быть сравним с конкретным подклассом данного трейта.
trait Container extends Ordered[Container]
Тем не менее, сейчас требуется сравнение
def compare(that: Container): Int
И поэтому мы не можем получить доступ к конкретному подтипу, например:
class MyContainer extends Container
код не скомпилируется, так как мы определяем Ordered для Container, а не конкретный подтип.
Чтобы это согласовать, мы используем F-ограниченный полиморфизм.
trait Container[AСтранный тип! Но заметьте, как Ordered параметризован с помощью A, который сам по себе является Container[A]
class MyContainer extends Container[MyContainer]Они сейчас упорядочены:
scala> List(new MyContainer, new MyContainer, new MyContainer) res3: List[MyContainer] = List(MyContainer@30f02a6d, MyContainer@67717334, MyContainer@49428ffa) scala> List(new MyContainer, new MyContainer, new MyContainer).min res4: MyContainer = MyContainer@33dfeb30Учитывая, что все они являются подтипами Container[_], мы можем определить другой подкласс и создать смешанный список Container[_]:
scala> class YourContainer extends Container[YourContainer] < def compare(that: YourContainer) = 0 >defined class YourContainer scala> List(new MyContainer, new MyContainer, new MyContainer, new YourContainer) res2: List[Container[_ >: YourContainer with MyContainer : YourContainer with MyContainerОбратите внимание, как результирующий тип в настоящее время ограничен снизу YourContainer с MyContainer. Это работа системы вывода типов. Интересно, что этот тип не имеет дополнительного смысла, он только обеспечивает логическую нижнюю границу для списка. Что произойдет, если мы попытаемся использовать Ordered сейчас?
(new MyContainer, new MyContainer, new MyContainer, new YourContainer).min :9: error: could not find implicit value for parameter cmp: Ordering[Container[_ >: YourContainer with MyContainer : YourContainer with MyContainerOrdered[] не существует для единого типа. Это слишком плохо.
Структурные типы
Scala имеет поддержку структурных типов — тип выражается интерфейсом structure вместо конкретного типа.
scala> def foo(x: < def get: Int >) = 123 + x.get foo: (x: AnyRef)Int scala> foo(new < def get = 10 >) res0: Int = 133Это может быть полезно во многих ситуациях, но реализация использует отражения, так что обращайте внимание на производительность.
Абстрактные типы членов
В трейте, вы можете оставить тип членов абстрактным.
scala> trait Foo < type A; val x: A; def getX: A = x >defined trait Foo scala> (new Foo < type A = Int; val x = 123 >).getX res3: Int = 123 scala> (new Foo < type A = String; val x = "hey" >).getX res4: java.lang.String = heyЧасто это полезный трюк, когда делается внедрение зависимостей, например.
Вы можете обратиться к абстрактному типу переменной, используя хеш-оператор:
scala> trait Foo[M[_]] < type t[A] = M[A] >defined trait Foo scala> val x: Foo[List]#t[Int] = List(1) x: List[Int] = List(1)Тип очистки и манифесты
Как вы знаете, информация о типе теряется во время компиляции благодаря очистке. Одна из особенностей Scala — это Манифесты, которые позволяют выборочно восстановить информацию о типе. Манифесты предоставляются в качестве неявного значения, которое генерируется компилятором по мере необходимости.
scala> class MakeFoo[A](implicit manifest: Manifest[A]) < def make: A = manifest.erasure.newInstance.asInstanceOf[A] >scala> (new MakeFoo[String]).make res10: String =Пример: Finagle
trait Service[-Req, +Rep] extends (Req => Future[Rep]) trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut]) < def andThen[Req2, Rep2](next: Filter[ReqOut, RepIn, Req2, Rep2]) = new Filter[ReqIn, RepOut, Req2, Rep2] < def apply(request: ReqIn, service: Service[Req2, Rep2]) = < Filter.this.apply(request, new Service[ReqOut, RepIn] < def apply(request: ReqOut): Future[RepIn] = next(request, service) override def release() = service.release() override def isAvailable = service.isAvailable >) > > def andThen(service: Service[ReqOut, RepIn]) = new Service[ReqIn, RepOut] < private[this] val refcounted = new RefcountedService(service) def apply(request: ReqIn) = Filter.this.apply(request, refcounted) override def release() = refcounted.release() override def isAvailable = refcounted.isAvailable >>Можно определить запросы с помощью filter.
trait RequestWithCredentials extends Request < def credentials: Credentials >class CredentialsFilter(credentialsParser: CredentialsParser) extends Filter[Request, Response, RequestWithCredentials, Response] < def apply(request: Request, service: Service[RequestWithCredentials, Response]): Future[Response] = < val requestWithCredentials = new RequestWrapper with RequestWithCredentials < val underlying = request val credentials = credentialsParser(request) getOrElse NullCredentials >service(requestWithCredentials) > >Обратите внимание, как основной сервис требует определения запроса, и что это проверяется статически. Фильтры можно рассматривать как преобразователи.
Множество фильтров могут быть объединены вместе:
val upFilter = logTransaction andThen handleExceptions andThen extractCredentials andThen homeUser andThen authenticate andThen routeПишите безопасный код!
Built at @twitter by @stevej, @marius, and @lahosken with much help from @evanm, @sprsquish, @kevino, @zuercher, @timtrueman, @wickman, @mccv and @garciparedes; Russian translation by appigram; Chinese simple translation by jasonqu; Korean translation by enshahar;
Руководство по Scala. Классы и объекты.
В данной статье мы рассмотрим классы и объекты в языке программирования Scala.
Класс является шаблоном для объектов. Когда мы создали класс, мы получаем возможность создавать его экземпляры с помощью ключевого слова new. Данный экземпляр (объект) имеет все свойства и методы, которые имеет класс.
Класс
Предположим, что у нас есть класс Developer, который имеет следующий вид:
package net.proselyte.scalatutorial.classes /** * Simple class that represents a Developer. * * @author Eugene Suleimanov */ class Developer(val name: String, val specialty: String) < var developerName: String = name; var developerSpecialty = specialty; def writeCode() < println(this.developerSpecialty + " writes code."); >>И клиентский класс DeveloperDemo, который выглядит следующим образом:
package net.proselyte.scalatutorial.classes /** * Demo object that demonstrates work of class Developer. * * @author Eugene Suleimanov */ object DeveloperDemo < def main(args: Array[String]) < val javaScalaDeveloper = new Developer("Eugene Suleimanov", "Java/Scala Developer"); println("Developer name: " + javaScalaDeveloper.developerName); println("Developer specialty: " + javaScalaDeveloper.developerSpecialty); javaScalaDeveloper.writeCode(); >>В результате выполнения DeveloperDemo, мы получим следующий результат:
Developer name: Eugene Suleimanov Developer specialty: Java/Scala Developer Java/Scala Developer writes code. Process finished with exit code 0Наследование
Как и во всех других ООП языках программирования, в Scala поддерживается механизм наследования. Для этого используется ключевое слово extends, как и в языка Java.
Но, в Scala есть два ограничения:
- Только первичный конструктор может передавать параметры в базовый конструктор
- Переопределение метода требует использования ключевого слова override.
Для понимания того, как это работает на практике, рассмотрим простой пример:
Предположим, что у нас есть класс Program и есть класс, который является его наследником SecuredProgam.
package net.proselyte.scalatutorial.classes /** * Simple class that represents a Program * * @author Eugene Suleimanov */ class Program(val name: String) < var programName: String = name; def startWork() < println("Hello, I'm a simple program. My name is: " + this.name); >>
package net.proselyte.scalatutorial.classes /** * Secured program that extends class Program. * * @author Eugene Suleimanov */ class SecuredProgram(override val name: String, val securityCertificate: String) extends Program(name) < override def startWork() < println("Hello, I'm a secured program. My name is: " + this.name); println("I have security certificate: " + this.securityCertificate); >>
package net.proselyte.scalatutorial.classes /** * Client class that demonstrates work of classes Program and SecuredProgram. * * @author Eugene Suleimanov */ object ProgramDemo < def main(args: Array[String]) < var program = new Program("Simple Program"); program.startWork(); var securedProgram = new SecuredProgram("Secured Program", "Security Certificate"); securedProgram.startWork(); >>
В результате выполнения данной программы мы получим, примерно, следующий результат:
Hello, I'm a simple program. My name is: Simple Program Hello, I'm a secured program. My name is: Secured Program I have security certificate: Security Certificate Process finished with exit code 0
Скрытые классы
Скрытые классы – это классы, которые позволяют нам неявное обращение к первичному конструктору, если класс находится в области его видимости.
Для создания неявного класса используется ключевое слово implicit.
При работе со скрытыми классами стоит учитывать следующее:
- Скрытые классы должны быть объявлены внутри другого класса, объекта или трейта.
- Скрытый класс может принимать только один не скрытый аргумент и конструктор.
Рассмотрим простой пример:
Предположим, что у нас есть следующие классы:
Squaring:
package net.proselyte.scalatutorial.classes /** * Simple object that provides multiplication. * * @author Eugene Suleimanov */ object Squaring < implicit class Squarer(x: Int) < def numberSquaring[A](f: =>A): Unit = < println(x + " * " + x + " writes code."); >>
И клиентский класс DeveloperDemo, который выглядит следующим образом:
package net.proselyte.scalatutorial.classes /** * Demo object that demonstrates work of class Developer. * * @author Eugene Suleimanov */ object DeveloperDemo < def main(args: Array[String]) < val javaScalaDeveloper = new Developer("Eugene Suleimanov", "Java/Scala Developer"); println("Developer name: " + javaScalaDeveloper.developerName); println("Developer specialty: " + javaScalaDeveloper.developerSpecialty); javaScalaDeveloper.writeCode(); >>
В результате выполнения DeveloperDemo, мы получим следующий результат:
Developer name: Eugene Suleimanov Developer specialty: Java/Scala Developer Java/Scala Developer writes code. Process finished with exit code 0
На этом мы заканчиваем обзор классов и объектов в Scala.
В следующей статье мы рассмотрим модификаторы доступа в данном языке программирования.
Основы модели ООП в Scala
Scala имеет гибкую ООП-модель, которая состоит из трейтов, обжектов, классов и кейс-классов, то есть модель чем-то напоминает аналогичную в Java. Вот, например, объявления класса:
class ClassName(field1: Type1, field2: Type2, … , fieldN: TypeN) // class body >Объявленный таким образом класс инстанцируется посредством конструкции new ClassName(field1, field2, … , fieldN). Параметры, которые вводятся в скобках после имени класса, — private-поля класса, но мы можем изменить область их видимости. К примеру, если нам надо обеспечить доступ на чтение извне, то перед именем поля следует поставить модификатор val. Если надо обеспечить запись и чтение — модификатор var. Классы в Scala являются ссылочными типами и наследуют базовый класс AnyRef (неявно). Таким образом, присваивание объекта с мутирующими (var) полями другой переменной лишь копирует ссылку.
Внутри класса мы можем вводить многие вложенные структуры, необходимые для внутреннего использования. При этом по умолчанию все переменные, методы и постоянные, которые объявлены в теле класса, являются публичными. Мы можем их защитить посредством модификаторов private и protected. Что касается вложенных классов, то они извне недоступны.
Вторая ООП-сущность — trait. Trait, в отличие от класса, не может иметь конструктора, следовательно, не может быть инстанцирован. Вдобавок к этому, он способен содержать абстрактные методы, тогда как класс — лишь в том случае, если помечен модификатором abstract.
Рассмотрим синтаксис трейта:
trait TraitName >Существует ещё одна ООП-сущность, которую мы рассматривали в этой статье. Речь идёт об object. Его отличительная черта — наличие единственного экземпляра, создаваемого автоматически, то есть он является синглтоном. Лишь object может быть точкой входа в программу. Вдобавок к этому, можно импортировать его вложенные типы, значения и методы в область видимости другого файла. Всё это даёт возможность применять object в качестве средства структурной организации программы, что в свою очередь даёт возможность группировать статические методы и константы.
Особенности наследования в Scala
Особенностей несколько: во-первых, почти всё можно наследовать от нескольких trait посредством синтаксиса extends trait1 with trait2 with trait3 . with traitN.
object HelloWorld extends AppВ нашем случае App является предопределённым трейтом, оборачивающим содержимое внутрь метода main.
Зачем нам нужен синтаксис extends … with … with …? Может, мы можем обойтись одним ключевым словом? К сожалению, не можем. Если же мы разрешим множественное наследование, то столкнёмся с проблемой ромбовидного наследования. В языке программирования Scala данная проблема решается путём определения главного трейта, реализация которого и наследуется. Речь идёт о трейте, который идёт первым после extends (кстати, это может быть и не трейт вовсе, а класс либо абстрактный класс).
Также мы не можем наследовать типы от объекта.
По правде говоря, у нас есть возможность создать анонимный класс-наследник трейта и реализовать все его абстрактные методы с помощью функционала анонимных классов, а потом создать его экземпляр:
trait T <> val t = new T // имплементация абстрактных методов >Однако писать данный код бывает весьма громоздким занятием. Для этого в Scala есть companion object — объекты-компаньоны. Для них не существует специального ключевого слова, поэтому для их определения надо создать в файле с классом либо трейтом object с тем же именем, что и у класса. У такого обжекта-компаньона вы сможете определить метод apply:
trait Foo def bar: Int > object Foo def apply(int: Int) = new Foo override def bar = int > >Тогда появится возможность создавать «экземпляры» трейта посредством синтаксиса Foo.apply(1) .
Так как этот код громоздкий, специально для метода apply разрешили не писать его название. Данная «магия» работает для всего, что называют apply в любой сущности. Вот, как это выглядит:
Foo(1)То же самое мы можем делать и с классами. Также следует заметить, что семантика метода apply может существенно отличаться от вышеприведённого примера, ведь никто не ограничивает в том, что именно будет делать этот метод. Данная особенность активно применяется и в стандартных, и в сторонних библиотеках.
Материал написан на основании статьи Ивана Камышана"Основы функционального программирования с примерами на Scala — часть 2".