Приховані риси Скали


149

Які приховані особливості Scala, про які повинен знати кожен розробник Scala?

Будь ласка, одна прихована функція на відповідь.


6
Хе, це питання настільки ж корисне, як його посилання на інші приховані функції публікацій, як і на саме питання. Ура!
JohnMetta

1
@mettadore просто подивіться на відповідні посилання праворуч.
Даніель К. Собрал

2
@JohnMetta: Або використовувати тег .

Відповіді:


85

Гаразд, мені довелося додати ще одне. Кожен Regexоб’єкт у Scala має витяжку (див. Відповідь з oxbox_lakes вище), що дає вам доступ до груп відповідностей. Тож ви можете зробити щось на кшталт:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

Другий рядок виглядає заплутаним, якщо ви не звикли використовувати відповідність шаблонів та витяжки. Щоразу, коли ви визначаєте a valабо var, те, що відбувається після ключового слова, - це не просто ідентифікатор, а скоріше шаблон. Ось чому це працює:

val (a, b, c) = (1, 3.14159, "Hello, world")

Правий вираз створює знак, Tuple3[Int, Double, String]який може відповідати візерунку (a, b, c).

Більшу частину часу ваші зразки використовують витяжки, які є членами одиночних об'єктів. Наприклад, якщо ви пишете такий зразок

Some(value)

то ви неявно викликаєте витяжку Some.unapply.

Але ви також можете використовувати екземпляри класу у шаблонах, і ось що тут відбувається. Регекс val - це екземпляр Regex, і коли ви використовуєте його за шаблоном, ви неявно викликаєте regex.unapplySeq( unapplyпроти того, unapplySeqяк виходить за межі цієї відповіді), який витягує групи збігів у Seq[String], елементи яких призначаються для того, щоб змінних рік, місяць та день.


1
Thx для публікації цього! FYI згадується у розділі "Витяг з регулярними виразами" у книзі "Програмування в Scala" на сторінці 503 у першому виданні та на сторінці 611 у другому виданні.
земляний пав

51

Визначення структурних типів - тобто тип, описаний методами, які він підтримує. Наприклад:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

Зауважте, що тип параметра closeableне визначений, окрім closeметоду


1
Структурні типи навіть не згадуються в "Програмуванні в Scala". Вони трохи повільніше, ніж інші методи для передачі типів, хоча вони використовують відображення для виклику правильних методів. (Сподіваємось, вони придумають спосіб пришвидшити це.)
Кен Блум

1
І також можна зробити псевдонім для них, що працює як зовнішньо призначений інтерфейс (дуже повільний): введіть Closeable = {def close (): Unit}
Олексій

45

Поліморфізм типу конструктора (він же типів вищого роду)

Без цієї функції ви можете, наприклад, висловити ідею відображення функції над списком для повернення іншого списку або відображення функції над деревом для повернення іншого дерева. Але ви не можете висловити цю думку взагалі без вищих видів.

З вищими видами ви можете зафіксувати ідею будь-якого типу , параметризованого для іншого типу. Кажуть, що конструктор типу, який приймає один параметр, є добрим (*->*). Наприклад, List. Кажуть, що конструктор типу, який повертає інший конструктор іншого типу, є добрим (*->*->*). Наприклад, Function1. Але у Scala у нас є більш високі типи, тому ми можемо мати конструктори типів, параметризовані з іншими конструкторами типу. Так вони подібні ((*->*)->*).

Наприклад:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

Тепер, якщо у вас є Functor[List], ви можете складати карти за списками. Якщо у вас є Functor[Tree], ви можете зробити карту дерев. Але ще важливіше, якщо у вас є Functor[A] будь-який з видів A(*->*) , ви можете зіставити функцію A.


39

Екстрактори, які дозволяють замінити безладний if-elseif-elseкод стилю візерунками. Я знаю, що це не зовсім приховано, але я використовую Scala протягом декількох місяців, не розуміючи, наскільки сильний. Для (тривалого) прикладу я можу замінити:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

З цим, що на мою думку набагато зрозуміліше

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

Я маю зробити трохи ніжок на задньому плані ...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

Але ніжка варто того, що вона відокремлює частину ділової логіки на розумне місце. Я можу реалізувати свої Product.getCodeметоди так:

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}

це не схоже на перемикач? можливо, це могло б переробити більше.
Гео

14
Візерунки схожі на
турбонапряжені

1
Приємно, але мені не подобається, що вам доведеться використовувати неявно, тому що його обсяг доходить далі, ніж відповідність {}. Ви також можете просто додати метод ProductService, який шукає продукт за кодом. Ви зможете все-таки обернути свій відреставрований фрагмент методом, щоб мати можливість його використовувати скрізь.
Мартін Конічек

35

Маніфести, які є своєрідним способом отримання інформації про тип під час виконання, як ніби Scala переосмислює типи.


8
Я думаю, що краще відповісти на пояснення у відповіді, а не на посилання. До речі, привіт agai oxbow! :-)
Даніель К. Собрал

Це справді прихована функція ... навіть не в документах API. Дуже корисно, хоча.
Андре Ласло

35

У програмі scala 2.8 ви можете використовувати хвостово-рекурсивні методи, використовуючи пакет scala.util.control.TailCalls (насправді це батут).

Приклад:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)

35

Класи справ автоматично змішуються в товарній ознаці, забезпечуючи нетиповий, індексований доступ до полів без будь-якого відображення:

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

Ця функція також забезпечує спрощений спосіб змінити результат toStringметоду:

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 

32

Це не зовсім приховано, але, безумовно, недорекламована функція: scalac -Xprint .

Як ілюстрацію використання розглянемо наступне джерело:

class A { "xx".r }

Компілюючи це за допомогою scalac -Xprint: typer outputs :

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

Зауважте scala.this.Predef.augmentString("xx").r, що це додаток implicit def augmentStringсьогодення у Predef.scala.

scalac -Xprint: <phase> надрукує синтаксичне дерево після деякої фази компілятора. Щоб побачити доступні фази, використовуйте scalac -Xshow-фази .

Це чудовий спосіб дізнатися, що відбувається за лаштунками.

Спробуйте з

case class X(a:Int,b:String)

використовуючи фазу друку, щоб справді відчути, наскільки це корисно.


30

Ви можете визначити власні структури управління. Це дійсно просто функції та предмети, а також якийсь синтаксичний цукор, але вони виглядають і поводяться як справжня річ.

Наприклад, наступний код визначає dont {...} unless (cond)і dont {...} until (cond):

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

Тепер ви можете зробити наступне:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 

Ще кілька прикладів тут: programmers.stackexchange.com/questions/13072/…
missingfaktor

Мені буде цікаво, якщо хтось знає спосіб визначення блоків if-then-else за допомогою додаткової іншої, що перевіряє тип, як стандартний.
Філіп

@Philippe : zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero. Потрібен Scalaz.
зниклийфактор

26

@switch примітка в Scala 2.8:

Анотація, яку слід застосувати до виразу відповідника. Якщо він присутній, компілятор перевірить, що збіг був складений на перемикач таблиць або lookupswitch, і видасть помилку, якщо замість цього компілюється у ряд умовних виразів.

Приклад:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {

26

Данно, якщо це справді приховано, але я вважаю це досить приємним.

Конструктори типів, які приймають два типи параметрів, можуть записуватися у позначення інфіксу

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}

1
Приємно! Я можу собі уявити, що бути корисним часом для покращення читабельності. Наприклад var foo2barConverter: Foo ConvertTo Bar, зробить порядок параметрів типу самоочевидним.
Еско Луонтола

4
Я іноді роблю це в коді, який певною мірою використовує PartialFunction: наберіть ~> [A, B] = PartialFunction [A, B]
raichoo

24

Скала 2.8 представила аргументи за замовчуванням та названі аргументи, що дозволило додати новий метод "копіювання", який Scala додає до класів справ. Якщо ви визначите це:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

і ви хочете створити новий Foo, подібний до існуючого Foo, лише з іншим значенням "n", тоді ви можете просто сказати:

foo.copy(n = 3)

3
ПОПЕРЕДЖЕННЯ: метод копіювання не буде перекрито, якщо ви успадковуєте один клас випадку від іншого. Тож вам доведеться перекрити це вручну
Олексій

Пов'язаний: прибиральник спосіб відновлення вкладених структур stackoverflow.com/q/3900307/203968
oluies

5
клас класів більше не є (Scala 2.8), дозволений успадковувати від класу case. Дякую, владико Скали, що зневажив цю нечесну спадщину.
olle kullberg

24

у scala 2.8 ви можете додати @specialized до своїх загальних класів / методів. Це створить спеціальні версії класу для примітивних типів (розширення AnyVal) та заощадить вартість непотрібних боксу / розпакування: class Foo[@specialized T]...

Ви можете вибрати підмножину AnyVals: class Foo[@specialized(Int,Boolean) T]...


1
Чи є довше пояснення, на яке ви могли б вказати мені? Я хотів би дізнатися більше.
Paweł Prażak

23

Розширення мови. Я завжди хотів щось подібне зробити на Java (не міг). Але в Scala я можу мати:

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

а потім напишіть:

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

і дістати

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)

23

Ви можете призначити для функції параметр виклику по імені (EDITED: це інакше, ніж параметр ледачий!), І він не буде оцінюватися, поки функція не буде використана (EDIT: насправді він буде переоцінюватися щоразу, коли це буде б / в). Дивіться цю довідку для отримання докладної інформації

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/

Я подумав, що "x: => Bar" означає, що x - це функція, яка не приймає жодних параметрів і повертає Bar. Отже, "нова смужка (22)" - це лише анонімна функція, яка оцінюється як функція, як і будь-яка інша функція.
Алекс Чорний

1
"x: () => Bar" визначає функцію xa, яка не приймає жодних параметрів і повертає Bar. x: => Бар визначає x як дзвінок по імені. Подивіться на scala.sygneca.com/faqs/… для більш детальної інформації
agilefall

3
Що ви показуєте, це параметри заклику по імені. Параметри ледачих
ArtemGr

Я думаю, що ти можеш використовувати це як ледачий парам, якщо ти робиш щось на кшталт lazy val xx: Bar = xсвого методу і з цього моменту ти лише використовуєш xx.
Крістіан Врабі

20

Ви можете використовувати locallyдля введення локального блоку, не викликаючи проблем з висновками з комою.

Використання:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally визначається в "Predef.scala" як:

@inline def locally[T](x: T): T = x

Будучи вбудованим, це не накладає додаткових накладних витрат.


3
Це пояснюється краще stackoverflow.com/questions/3237727 / ...
Еско Luontola

17

Рання ініціалізація:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

Вихід:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

Ми створюємо анонімний внутрішній клас, ініціалізуючи valueполе в блоці, перед with AbstractT2клаузою. Це гарантує, що valueініціалізується перед виконанням тіла AbstractT2, як показано під час запуску сценарію.


1
Конструкція називається "ранньою ініціалізацією".
Рандалл Шульц,

17

Можна створити структурні типи за допомогою ключового слова "з"

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}

17

синтаксис заповнення заповнення для анонімних функцій

Із специфікації мови Scala:

SimpleExpr1 ::= '_'

Вираз (синтаксична категорія Expr) може містити вбудовані символи підкреслення _в місцях, де ідентифікатори є законними. Такий вираз являє собою анонімну функцію, де наступні виникнення підкреслення позначають послідовні параметри.

Від змін мови Scala :

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

Використовуючи це, ви можете зробити щось на кшталт:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))

2
Це слід називати «синтаксисом заповнення заповнення анонімних функцій». Імпліцит має чітке значення у Scala, і це не пов’язано з цим.
ретронім

Посилання має неочевидне відношення до відповіді. "неявна" - не правильний термін для цього. Як і вище, він повинен бути "заповнювачем".
Ален О'Деа

2
Це насправді не «приховано», я бачила це використання майже у всіх навчальних посібниках про Scala, які я прочитала ... :-) Але я ціную формальне визначення, якого я ще не бачила.
ФіЛхо

@PhiLho, можливо, це було менш відомим у 2009 році. Я не знаю.
Євген Йокота

Я пропустив початкову дату, оскільки відображається лише остання дата редагування. І добре, не всі функції, пояснені в цій темі, "приховані". Класна нитка і хороша відповідь все одно.
PhiLho

16

Неявні визначення, зокрема перетворення.

Наприклад, припустимо функцію, яка відформатує вхідний рядок відповідно до розміру, замінивши його середину на "...":

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Ви можете використовувати це з будь-яким рядком і, звичайно, використовувати метод toString для перетворення будь-чого. Але ви також можете написати це так:

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

А потім ви можете пройти заняття інших типів, зробивши це:

implicit def double2String(d: Double) = d.toString

Тепер ви можете викликати цю функцію, передаючи подвійний:

sizeBoundedString(12345.12345D, 8)

Останній аргумент неявний і передається автоматично через неявну декларацію. Крім того, "s" трактується як String всередині sizeBoundedString, оскільки відбувається неявне перетворення з нього в String.

Імпліцити такого типу краще визначати для рідкісних типів, щоб уникнути несподіваних перетворень. Ви також можете експліцитно пройти конверсію, і вона все одно буде неявно використана всередині sizeBoundedString:

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

Ви також можете мати декілька неявних аргументів, але тоді ви повинні або пройти всі, або не пропустити жодного з них. Існує також синтаксис швидкого доступу для неявних перетворень:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Це використовується точно так само.

Імпліцити можуть мати будь-яке значення. Вони можуть бути використані, наприклад, для приховування інформації про бібліотеку. Візьмемо, наприклад, такий приклад:

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

У цьому прикладі виклик "f" в об'єкті Y передасть журнал демону за замовчуванням, а в екземплярі X - демону Daemon X. Але виклик g на екземпляр X відправить журнал у явно заданий DefaultDaemon.

Хоча цей простий приклад може бути переписаний із перевантаженням та приватним станом, наслідки не потребують приватного стану та можуть бути приведені в контекст імпорту.


13

Можливо, не надто приховано, але я вважаю, що це корисно:

@scala.reflect.BeanProperty
var firstName:String = _

Це автоматично генерує геттер і сетер для поля, яке відповідає умовам боба.

Подальший опис у розробниках


6
І ви можете зробити ярлик для нього, якщо ви багато використовуєте його, наприклад: import scala.reflect. {BeanProperty => BP}
Олексій

13

Неявні аргументи у закриттях.

Аргумент функції може бути позначений як неявний так само, як і методи. У межах області функції неявний параметр є видимим і придатним для неявного вирішення:

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}


12

Типи результатів залежать від неявної роздільної здатності. Це може дати вам форму декількох відправлень:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0

Це може бути так, але вищевказаний сеанс вводить в оману. Визначення fooвикористання, aяке повинно бути присутнім у середовищі до виконання цих команд. Я припускаю, що ви мали на увазі z.perform(x).
Даніель К. Собрал

4

Еквівалент Scala з ініціалізатором подвійної дужки Java.

Scala дозволяє створити анонімний підклас з тілом класу (конструктором), що містить заяви, щоб ініціалізувати примірник цього класу.

Цей шаблон дуже корисний при побудові користувальницьких інтерфейсів на основі компонентів (наприклад, Swing, Vaadin), оскільки дозволяє створювати компоненти інтерфейсу та декларувати їх властивості більш стисло.

Для отримання додаткової інформації див. Http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf .

Ось приклад створення кнопки Vaadin:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}

3

Виключення членів із importзаяв

Припустимо, ви хочете використовувати a, Loggerщо містить a printlnі printerrметод, але ви хочете використовувати одне лише для повідомлень про помилки, а зберегти старий добрий Predef.printlnдля стандартного виводу. Ви можете це зробити:

val logger = new Logger(...)
import logger.printerr

але якщо loggerтакож є ще дванадцять методів, які ви хотіли б імпортувати та використовувати, перераховувати їх стає незручно. Ви можете замість цього спробувати:

import logger.{println => donotuseprintlnt, _}

але це все ще "забруднює" список імпортованих членів. Введіть потужну підстановку über:

import logger.{println => _, _}

і це зробить правильно все ™.


2

requireметод (визначений в Predef), що дозволяє визначати додаткові обмеження функції, які перевірялися б під час виконання. Уявіть, що ви розробляєте ще одного клієнта щебетання, і вам потрібно обмежити довжину твіту до 140 символів. Більше того, ви не можете публікувати порожній твіт.

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

Тепер виклик повідомлення з аргументом невідповідної довжини спричинить виняток:

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

Ви можете написати кілька вимог або навіть додати опис до кожної:

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

Зараз винятки є багатослівними:

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

Ще один приклад тут .


Бонус

Ви можете виконувати дію щоразу, коли вимога не вдається:

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1

1
requireне є застереженим словом. Це лише метод, визначений в Predef.
зниклий фактор

1

Риси abstract overrideметодів - особливість у Scala, яка не так широко рекламується, як багато інших. Метод методу з abstract overrideмодифікатором полягає в виконанні деяких операцій і делегуванні виклику в super. Тоді ці риси повинні бути змішані з конкретними реалізаціями своїх abstract overrideметодів.

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

Незважаючи на те, що мій приклад насправді не набагато більше, ніж АОП з бідними людьми, я використовував ці складові риси сильно на свій смак, щоб створити екземпляри інтерпретатора Scala із заздалегідь визначеними імпортами, спеціальними прив'язками та classpathes. У стекові риси характеру дозволив створити свій завод по лініях , new InterpreterFactory with JsonLibs with LuceneLibsа потім мають корисний імпорт і сфера varibles для сценаріїв користувачів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.