Scala'nın Gizli Özellikleri


149

Scala'nın her Scala geliştiricisinin bilmesi gereken gizli özellikleri nelerdir?

Cevap başına bir gizli özellik lütfen.


6
Heh, bu soru, sorunun kendisi için olduğu gibi diğer gizli özellik yayınlarına bağlantıları için de yararlıdır. Şerefe!
JohnMetta

1
@mettadore sadece sağ taraftaki ilgili bağlantılara bakın .
Daniel C.Sobral

2
@JohnMetta: Veya etiketi kullanın .

Yanıtlar:


85

Tamam, bir tane daha eklemek zorunda kaldım. RegexScala'daki her nesnede, size maç gruplarına erişmenizi sağlayan bir çıkarıcı vardır (yukarıdaki oxbox_lakes'den gelen cevaba bakınız). Böylece şöyle bir şey yapabilirsiniz:

// 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"

İkinci satır, desen eşleştirme ve çıkarıcılar kullanmaya alışkın değilseniz kafa karıştırıcı görünür. Bir valveya tanımladığınızda var, anahtar kelimeden sonra gelenler yalnızca bir tanımlayıcı değil, bir kalıptır. Bu yüzden bu işe yarıyor:

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

Sağ el ifadesi Tuple3[Int, Double, String], kalıpla eşleşebilecek bir ifade oluşturur (a, b, c).

Çoğunlukla kalıplarınız tekil nesnelerin üyesi olan çıkarıcılar kullanır. Örneğin, şöyle bir desen yazarsanız

Some(value)

o zaman dolaylı olarak çıkarıcıyı çağırıyorsunuz Some.unapply.

Ancak sınıf örneklerini kalıplarda da kullanabilirsiniz ve burada olan da budur. Val regex bir örneğidir Regexve bir desende bunu kullandığınızda, örtük aradığınız regex.unapplySeq( unapplyversus unapplySeqbir içine maç gruplarını ayıklar, bu yanıt kapsamı dışındadır) Seq[String], amacıyla tahsis edildiği unsurlarını yıl, ay ve gün değişkenleri.


1
Bunu göndermek için teşekkürler! FYI, birinci baskıda "Scala'da Programlama" sayfa 503 ve ikinci baskıda sayfa 611 başlıklı kitapta yer alan "Normal ifadelerle ayıklama" bölümünde belirtilmiştir.
dünyevi paul

51

Yapısal tip tanımları - yani hangi yöntemleri desteklediğini açıklayan tip. Örneğin:

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

Parametrenin türününcloseable bir closeyöntemi dışında tanımlanmadığına dikkat edin


1
"Scala'da Programlama" bölümünde yapısal türlerden bahsedilmemiştir. Doğru yöntemleri çağırmak için yansıma kullandıklarından, geçen türler için diğer tekniklerden biraz daha yavaştırlar. (Umarım bunu hızlandırmak için bir yol
bulurlar

1
Ayrıca, harici olarak atanan arayüz (çok yavaş olan) gibi çalışan, onlar için takma ad yapmak da mümkündür: type Closeable = {def close (): Unit}
Alexey

45

Tip-Yapıcı Polimorfizmi (daha yüksek türler olarak da bilinir)

Bu özellik olmadan, örneğin, başka bir listeyi döndürmek için bir işlevi bir liste üzerinde eşleme veya başka bir ağacı döndürmek için bir işlevi ağaç üzerinde eşleştirme fikrini ifade edebilirsiniz. Ancak bu fikri genellikle daha yüksek türler olmadan ifade edemezsiniz .

Daha yüksek türlerle, başka bir türle parametrelenen herhangi bir tür fikrini yakalayabilirsiniz . Bir parametre alan bir tür yapıcısının türünün olduğu söylenir (*->*). Örneğin List,. Başka bir tür yapıcı döndüren bir tür yapıcısının tür olduğu söylenir (*->*->*). Örneğin Function1,. Ancak Scala'da daha yüksek çeşitlerimiz var, bu yüzden diğer tip kurucularla parametreleştirilmiş tip kurucularımız olabilir. Yani bunlar gibi ((*->*)->*).

Örneğin:

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

Şimdi, a'nız varsa Functor[List]listeler üzerinde harita oluşturabilirsiniz. Eğer varsa, Functor[Tree]ağaçların üzerinden harita çizebilirsiniz. Ama daha da önemlisi, Functor[A] herhangi bir A çeşidiniz(*->*) varsa, bir işlevi eşleyebilirsiniz A.


39

Dağınık if-elseif-elsestil kodunu desenlerle değiştirmenizi sağlayan aspiratörler . Bunların tam olarak gizli olmadığını biliyorum ama birkaç aydır Scala'yı gerçekten gücünü anlamadan kullanıyorum. (Uzun) bir örnek için ben değiştirebilirsiniz:

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)
}

Bu, bence çok daha net

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

Arka planda biraz ayak işi yapmalıyım ...

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
    }
  }

Ancak, iş parçası, bir iş mantığını bir parçasını mantıklı bir yere ayırdığı için buna değer. Product.getCodeYöntemlerimi aşağıdaki gibi uygulayabilirim .

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

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

bu bir anahtar gibi değil mi? belki bu daha fazla yeniden düzenlenebilir.
Geo

14
Desenler turbo şarjlı anahtarlar gibidir: çok daha güçlü ve net
oxbow_lakes

1
Güzel, ama örtük kullanmanızdan hoşlanmıyorum çünkü kapsamı {} maçından daha uzağa ulaşıyor. Ayrıca, ProductService'e, bir Ürünü koduna göre arayan bir yöntem de ekleyebilirsiniz. Yeniden düzenlenmiş snippet'inizi her yerde kullanabilmek için yine de bir yönteme sarmış olursunuz.
Martin Konicek

35

Scala türleri birleştirmiş gibi, çalışma zamanında tür bilgilerini almanın bir yolu olan tezahürler .


8
Bunun cevabı açıklamak için tercih olduğunu düşünüyorum içinde yerine bir linke atıfta daha cevap. Bu arada, merhaba agai oxbow! :-)
Daniel C.Sobral

Bu gerçekten gizli bir özellik ... API belgelerinde bile. Yine de çok yararlı.
André Laszlo

35

Skala 2.8'de scala.util.control.TailCalls paketini kullanarak kuyruk özyinelemeli yöntemlere sahip olabilirsiniz (aslında trampolindir).

Bir örnek:

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

Vaka sınıfları otomatik olarak Ürün özelliğini karıştırır ve alanlara yansıma olmadan türlenmemiş, dizine alınmış erişim sağlar:

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)

Bu özellik ayrıca toStringyöntemin çıktısını değiştirmek için basitleştirilmiş bir yol sağlar :

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

Tam olarak gizli değil, ama kesinlikle reklamı altında bir özellik: scalac -Xprint .

Kullanımın bir örneği olarak aşağıdaki kaynağı göz önünde bulundurun:

class A { "xx".r }

Bunu scalac -Xprint: typer çıktılarıyla derleme :

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

Uyarı scala.this.Predef.augmentString("xx").rbir uygulamadır, implicit def augmentStringPredef.scala içinde mevcut.

scalac -Xprint: <faz> , bir derleyici aşamasından sonra sözdizimi ağacını yazdırır. Kullanılabilir aşamaları görmek için scalac -Xshow-aşamalarını kullanın .

Bu, perde arkasında neler olup bittiğini öğrenmenin harika bir yoludur.

İle deneyin

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

gerçekten ne kadar yararlı olduğunu hissetmek için typer aşamasını kullanmak .


30

Kendi kontrol yapılarınızı tanımlayabilirsiniz. Gerçekten sadece fonksiyonlar ve nesneler ve bazı sözdizimsel şeker, ama gerçek şey gibi görünüyorlar ve davranıyorlar.

Örneğin, aşağıdaki kod dont {...} unless (cond)ve dont {...} until (cond)şunları tanımlar :

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
  }
}

Şimdi aşağıdakileri yapabilirsiniz:

/* 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) 


Birisi isteğe bağlı başka standart bloklar gibi tip kontrol ile if-then-else blokları tanımlamak için bir yol biliyor olup olmadığını merak ediyorum.
Philippe

@Philippe: zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero. Skaler gerektirir.
missingfaktor

26

@switch Scala 2.8'deki açıklama:

Eşleşme ifadesine uygulanacak bir ek açıklama. Varsa, derleyici eşleşmenin bir tablo anahtarına veya arama anahtarına derlendiğini doğrular ve bunun yerine bir dizi koşullu ifadeye derlenirse bir hata verir.

Misal:

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

Bu gerçekten gizli ise Dunno, ama oldukça güzel buluyorum.

2 tip parametre alan tip yapıcılar infix gösteriminde yazılabilir

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
Güzel! Okunabilirliği iyileştirmede bazen yararlı olabileceğini hayal edebiliyorum. Örneğin var foo2barConverter: Foo ConvertTo Bar, tür parametrelerinin sırasını açıkça gösterir.
Esko Luontola

4
Bazen bir dereceye kadar PartialFunction kullanan kodda yapmak: type ~> [A, B] = PartialFunction [A, B]
raichoo

24

Scala 2.8, Scala'nın vaka sınıflarına eklediği yeni bir "kopya" yönteminin eklenmesini mümkün kılan varsayılan ve adlandırılmış argümanları tanıttı. Bunu tanımlarsanız:

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

ve sadece farklı bir "n" değerine sahip, mevcut bir Foo gibi yeni bir Foo oluşturmak istiyorsanız, şunu söyleyebilirsiniz:

foo.copy(n = 3)

3
UYARI: bir vaka sınıfını diğerinden devralmanız durumunda kopyalama yöntemi geçersiz kılmayacaktır. Bu yüzden manuel olarak geçersiz kılmalısınız
Alexey

İlgili: İç içe geçmiş yapıları güncellemenin daha temiz yolu stackoverflow.com/q/3900307/203968
oluies

5
vaka sınıfının artık (Scala 2.8) bir vaka sınıfından miras almasına izin verilmiyor. Scala'nın efendisi bu kutsal olmayan mirası reddettiği için teşekkür ederim.
olle kullberg

24

scala 2.8'de genel sınıflarınıza / yöntemlerinize @specialized ekleyebilirsiniz. Bu, ilkel türler (AnyVal'ı genişleterek) için sınıfın özel sürümlerini oluşturur ve gereksiz boks / kutulama maliyetinden tasarruf eder: class Foo[@specialized T]...

AnyVals'ın bir alt kümesini seçebilirsiniz: class Foo[@specialized(Int,Boolean) T]...


1
Beni işaret edebileceğin daha uzun bir açıklama var mı? Daha fazlasını öğrenmek istiyorum.
Paweł Prażak

23

Dili genişletme. Her zaman Java'da böyle bir şey yapmak istedim (yapamadım). Ama Scala'da sahip olabilirim:

  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
  }

ve sonra şunu yazın:

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

ve Al

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

23

Bir işleve bir Call-by-name parametresi (EDITED: bu sonra tembel bir parametreden farklıdır!) Atayabilirsiniz ve işlev tarafından kullanılana kadar değerlendirilmez (EDIT: aslında, her seferinde yeniden değerlendirilir. Kullanılmış). Ayrıntılar için bu SSS'ye bakın

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" ın x'in parametre almayan ve bir Bar döndüren bir işlev olduğu anlamına geldiğini düşündüm. Dolayısıyla, "yeni çubuk (22)" sadece anonim bir işlevdir ve diğer işlevler gibi bir işlev olarak değerlendirilir.
Alex Black

1
"x: () => Çubuk" parametre almayan ve Çubuk döndüren xa işlevini tanımlar. x: => Çubuk, x'i adıyla çağrı olarak tanımlar. Daha fazla bilgi için scala.sygneca.com/faqs/… adresine bir göz atın
Agilefall

3
Gösterdiğiniz, isimle çağrı parametreleri. Tembel parametreler henüz uygulanmadı: lampvn.epfl.ch/trac/scala/ticket/240
ArtemGr

Metodunuzda olduğu gibi bir şey yaparsanız lazy val xx: Bar = xve o andan itibaren sadece kullanırsanız tembel bir param olarak kullanabileceğinizi düşünüyorum xx.
Cristian Vrabie

20

locallyNoktalı virgül çıkarım sorunlarına neden olmadan yerel bir blok tanıtmak için kullanabilirsiniz .

Kullanımı:

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" da şöyle tanımlanır:

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

Satır içi olduğundan, ek yük getirmez.



17

Erken Başlatma:

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)

Çıktı:

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

Anonimden valueönce, bloktaki alanı başlatan anonim bir iç sınıf başlatırız with AbstractT2. Bu , komut dosyasını çalıştırdığınızda gösterildiği gibi value, gövdesinin AbstractT2yürütülmesinden önce başlatıldığını garanti eder .


1
Yapı "erken başlatma" olarak adlandırılır.
Randall Schulz

17

'With' anahtar kelimesi ile yapısal türleri oluşturabilirsiniz

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

anonim işlevler için yer tutucu sözdizimi

Scala Dil Spesifikasyonundan:

SimpleExpr1 ::= '_'

Bir ifade (sözdizimsel kategoride Expr), _tanımlayıcıların yasal olduğu yerlerde gömülü alt çizgi sembolleri içerebilir . Böyle bir ifade, sonraki alt çizgi oluşumlarının ardışık parametreleri ifade ettiği anonim bir işlevi temsil eder.

Gönderen Scala Dil Değişiklikler :

_ + 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)

Bunu kullanarak şöyle bir şey yapabilirsiniz:

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

2
Bu, 'anonim işlevler için yer tutucu sözdizimi' olarak adlandırılmalıdır. Örtük Scala'da belirgin bir anlama sahiptir ve bununla ilgili değildir.
retronym

Bağlantının cevabı ile açık bir ilişkisi var. "örtük" bunun için doğru terim değil. Yukarıdaki gibi "yer tutucu" olmalıdır.
Alain O'Dea

2
Gerçekten "gizli" değil, ben Scala üzerinde okudum hemen hemen tüm öğreticiler bu kullanımı gördüm ... :-) Ama henüz görmedim resmi tanımı için teşekkür ederiz.
PhiLho

@PhiLho belki 2009 yılında daha az biliniyordu. Bilmiyorum.
Eugene Yokota

Yalnızca son düzenleme tarihi gösterildiği için orijinal tarihi kaçırdım. Ve bu konuda açıklanan tüm özellikler "gizli" değildir. Yine de serin iplik ve iyi cevap.
PhiLho

16

Örtük tanımlar, özellikle dönüşümler.

Örneğin, bir girdi dizesini ortasına "..." ile değiştirerek bir boyuta uyacak şekilde biçimlendirecek bir işlev olduğunu varsayalım:

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
}

Bunu herhangi bir String ile kullanabilir ve elbette, herhangi bir şeyi dönüştürmek için toString yöntemini kullanabilirsiniz. Ama bunu şöyle de yazabilirsiniz:

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
}

Ve sonra, bunu yaparak diğer türden sınıfları geçebilirsiniz:

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

Şimdi bu işlevi bir çift geçerek çağırabilirsiniz:

sizeBoundedString(12345.12345D, 8)

Son argüman örtüktür ve örtülü bildirim nedeniyle otomatik olarak iletilir. Bundan başka, "s" olan tedavi ondan String bir kapalı dönüştürme çünkü sizeBoundedString içinde bir String gibi.

Beklenmeyen dönüşümlerden kaçınmak için nadir türler için bu tür etkiler daha iyi tanımlanır. Ayrıca bir dönüşümü açık bir şekilde iletebilirsiniz ve yine de sizeBoundedString içinde örtük olarak kullanılacaktır:

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

Ayrıca birden çok örtük argümanınız da olabilir, ancak ya hepsini geçmeniz ya da hiçbirini geçmemeniz gerekir. Örtük dönüşümler için bir kısayol sözdizimi de vardır:

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
}

Bu tamamen aynı şekilde kullanılır.

Çıkarımların herhangi bir değeri olabilir. Örneğin kütüphane bilgilerini gizlemek için kullanılabilirler. Örneğin aşağıdaki örneği ele alalım:

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")
  }
}

Bu örnekte, Y nesnesindeki "f" çağrısı, günlüğü varsayılan arka plan programına ve X örneğinde Daemon X arka plan programına gönderir. Ancak X'in bir örneğinde g çağrılması, günlüğü açıkça verilen DefaultDaemon'a gönderir.

Bu basit örnek aşırı yük ve özel durumla yeniden yazılabilirken, imalar özel durum gerektirmez ve ithalatla bağlama getirilebilir.


13

Belki çok gizli değil, ama bunun yararlı olduğunu düşünüyorum:

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

Bu otomatik olarak fasülye kuralına uyan alan için bir alıcı ve ayarlayıcı oluşturur.

Developerworks'te daha fazla açıklama


6
Ve çok kullanıyorsanız kısayol yapabilirsiniz, örneğin: import scala.reflect. {BeanProperty => BP}
Alexey

13

Kapaklarda örtük argümanlar.

İşlev bağımsız değişkeni, yöntemlerde olduğu gibi örtük olarak işaretlenebilir. İşlev gövdesi kapsamında, örtük parametre görünür ve örtük çözümlemeye uygundur:

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

Sonuç türleri örtük çözünürlüğe bağlıdır. Bu size birden fazla gönderim şekli verebilir:

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

Durum böyle olabilir, ancak yukarıdaki oturum yanıltıcıdır. Nedir fookullanımları bir aönceki Bu komutların yürütülmesi için ortamda mevcut olmalı. Demek istediğini sanıyorum z.perform(x).
Daniel C. Sobral

4

Scala'nın Java çift ayraç başlatıcısı eşdeğeri.

Scala, sınıfın gövdesiyle (yapıcı) o sınıfın örneğini başlatmak için ifadeler içeren anonim bir alt sınıf oluşturmanıza olanak tanır.

Bu model, bileşen tabanlı kullanıcı arabirimleri (örneğin Swing, Vaadin) oluştururken, UI bileşenleri oluşturmasına ve özelliklerini daha kısa bir şekilde bildirmesine izin verdiği için çok kullanışlıdır.

Daha fazla bilgi için http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf adresine bakın .

Vaadin düğmesi oluşturma örneği:

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

3

Üyeleri importifadelerden hariç tutma

LoggerA printlnve printerryöntem içeren bir kullanmak istediğinizi , ancak yalnızca hata iletileri için kullanmak istediğinizi ve Predef.printlnstandart çıktı için eskiyi saklamak istediğinizi varsayalım . Bunu yapabilirsin:

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

ancak loggeriçe aktarmak ve kullanmak istediğiniz başka bir oniki yöntem daha içeriyorsa bunları listelemek zorlaşır. Bunun yerine deneyebilirsiniz:

import logger.{println => donotuseprintlnt, _}

ancak bu hâlâ ithal edilen üyelerin listesini "kirletiyor". Über-güçlü joker karaktere girin:

import logger.{println => _, _}

ve bu doğru olanı yapacak ™.


2

requirePredefçalışma zamanı sırasında denetlenecek ek işlev kısıtlamaları tanımlamanızı sağlayan yöntem (içinde tanımlanmış ). Başka bir twitter istemcisi geliştirdiğinizi ve tweet uzunluğunu 140 simgeye kadar sınırlamanız gerektiğini düşünün. Üstelik boş tweet gönderemezsiniz.

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

Şimdi uygunsuz uzunluk argümanıyla gönderi çağırmak bir istisnaya neden olacaktır:

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)

Birden çok gereksinim yazabilir veya hatta her birine açıklama ekleyebilirsiniz:

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

Şimdi istisnalar ayrıntılı:

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

Bir örnek daha burada .


Bonus

Gereksinim her başarısız olduğunda bir eylem gerçekleştirebilirsiniz:

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
requireayrılmış bir sözcük değil. Bu, içinde tanımlanan bir yöntemdir Predef.
missingfaktor

1

İle Özellikleri abstract overrideyöntemleri gibi yaygın diğerleri olarak bildirilmez Scala'da bir özelliğidir. Değiştirici ile yöntemlerin abstract overrideamacı bazı işlemler yapmak ve çağrıyı devretmektir super. Daha sonra bu özellikler, abstract overrideyöntemlerinin somut uygulamaları ile karıştırılmalıdır .

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

Örneğim gerçekten fakir bir adamın AOP'sinden çok daha fazla olmasa da, bu İstiflenebilir Özellikleri çok önceden tanımlanmış ithalatlar, özel bağlamalar ve sınıfyolları ile Scala yorumlayıcı örnekleri oluşturma isteğimle kullandım. İstif Özellikleri mümkün çizgisinde benim fabrika yaratmak için yapılmış new InterpreterFactory with JsonLibs with LuceneLibsve daha sonra kullanışlı ithalatı var ve kapsam kullanıcıları komut dosyaları için varibles.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.