Apa saja fitur tersembunyi Scala yang harus diperhatikan oleh setiap pengembang Scala?
Tolong, satu fitur tersembunyi per jawaban.
Apa saja fitur tersembunyi Scala yang harus diperhatikan oleh setiap pengembang Scala?
Tolong, satu fitur tersembunyi per jawaban.
Jawaban:
Oke, saya harus menambahkan satu lagi. Setiap Regex
objek di Scala memiliki extractor (lihat jawaban dari oxbox_lakes di atas) yang memberi Anda akses ke grup pertandingan. Jadi Anda dapat melakukan sesuatu seperti:
// 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"
Baris kedua terlihat membingungkan jika Anda tidak terbiasa menggunakan pencocokan pola dan ekstraktor. Setiap kali Anda mendefinisikan val
atau var
, apa yang muncul setelah kata kunci bukan hanya pengidentifikasi melainkan sebuah pola. Itu sebabnya ini bekerja:
val (a, b, c) = (1, 3.14159, "Hello, world")
Ekspresi tangan kanan membuat Tuple3[Int, Double, String]
yang dapat cocok dengan pola (a, b, c)
.
Sebagian besar waktu pola Anda menggunakan ekstraktor yang merupakan anggota objek tunggal. Misalnya, jika Anda menulis pola suka
Some(value)
maka Anda secara implisit memanggil ekstraktor Some.unapply
.
Tetapi Anda juga dapat menggunakan instance kelas dalam pola, dan itulah yang terjadi di sini. Val regex adalah turunan dari Regex
, dan ketika Anda menggunakannya dalam suatu pola, Anda secara implisit memanggil regex.unapplySeq
( unapply
versus unapplySeq
berada di luar cakupan jawaban ini), yang mengekstraksi grup pertandingan menjadi a Seq[String]
, elemen-elemen yang ditugaskan untuk variabel tahun, bulan, dan hari.
Definisi tipe struktural - yaitu tipe yang dijelaskan oleh metode apa yang didukungnya. Sebagai contoh:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
Perhatikan bahwa tipe parameter closeable
tidak didefinisikan selain memiliki close
metode
Tanpa fitur ini, Anda dapat, misalnya, mengekspresikan ide memetakan fungsi pada daftar untuk mengembalikan daftar lain, atau memetakan fungsi pada pohon untuk mengembalikan pohon lain. Tetapi Anda tidak dapat mengungkapkan ide ini secara umum tanpa jenis yang lebih tinggi.
Dengan jenis yang lebih tinggi, Anda dapat menangkap gagasan jenis apa pun yang diparameterisasi dengan jenis lain. Tipe konstruktor yang mengambil satu parameter dikatakan sejenis (*->*)
. Sebagai contoh List
,. Tipe konstruktor yang mengembalikan tipe konstruktor lain dikatakan sejenis (*->*->*)
. Sebagai contoh Function1
,. Tetapi dalam Scala, kami memiliki jenis yang lebih tinggi , sehingga kami dapat memiliki konstruktor tipe yang parameterised dengan konstruktor tipe lainnya. Jadi mereka seperti ((*->*)->*)
.
Sebagai contoh:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
Sekarang, jika Anda memiliki Functor[List]
, Anda dapat memetakan daftar. Jika Anda memiliki Functor[Tree]
, Anda dapat memetakan di atas pohon. Tetapi yang lebih penting, jika Anda memiliki Functor[A]
A jenis apa pun(*->*)
, Anda dapat memetakan fungsi A
.
Extractors yang memungkinkan Anda untuk mengganti if-elseif-else
kode gaya berantakan dengan pola. Saya tahu ini tidak sepenuhnya tersembunyi tetapi saya telah menggunakan Scala selama beberapa bulan tanpa benar-benar memahami kekuatan mereka. Untuk contoh (panjang) saya bisa mengganti:
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)
}
Dengan ini, yang jauh lebih jelas menurut saya
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
Saya harus melakukan sedikit kerja keras di latar belakang ...
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
}
}
Tetapi kerja keras itu sepadan dengan kenyataan bahwa ia memisahkan sepotong logika bisnis menjadi tempat yang masuk akal. Saya dapat menerapkan Product.getCode
metode saya sebagai berikut ..
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
Manifes yang merupakan semacam cara untuk mendapatkan informasi jenis saat runtime, seolah-olah Scala telah memverifikasi jenis.
Dalam scala 2.8 Anda dapat memiliki metode rekursif ekor dengan menggunakan paket scala.util.control.TailCalls (sebenarnya itu trampolining).
Sebuah contoh:
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)
Kelas kasus secara otomatis mencampur sifat Produk, memberikan akses terindeks, terindeks ke bidang tanpa refleksi:
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)
Fitur ini juga menyediakan cara yang disederhanakan untuk mengubah output dari toString
metode:
case class Person(name: String, age: Int) {
override def productPrefix = "person: "
}
// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28))
Ini tidak sepenuhnya tersembunyi, tetapi tentu saja fitur yang diiklankan di bawah: scalac -Xprint .
Sebagai ilustrasi penggunaan pertimbangkan sumber berikut:
class A { "xx".r }
Kompilasi ini dengan scalac -Xprint: output typer :
package <empty> {
class A extends java.lang.Object with ScalaObject {
def this(): A = {
A.super.this();
()
};
scala.this.Predef.augmentString("xx").r
}
}
Perhatikan scala.this.Predef.augmentString("xx").r
, yang merupakan aplikasi dari implicit def augmentString
hadiah di Predef.scala.
scalac -Xprint: <phase> akan mencetak pohon sintaks setelah beberapa fase kompiler. Untuk melihat fase yang tersedia, gunakan scalac -Xshow-phases .
Ini adalah cara yang bagus untuk mempelajari apa yang terjadi di balik layar.
Coba dengan
case class X(a:Int,b:String)
menggunakan fase typer untuk benar-benar merasakan manfaatnya.
Anda dapat menentukan struktur kontrol Anda sendiri. Ini benar-benar hanya fungsi dan objek dan beberapa gula sintaksis, tetapi mereka terlihat dan berperilaku seperti aslinya.
Misalnya, kode berikut mendefinisikan dont {...} unless (cond)
dan 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
}
}
Sekarang Anda dapat melakukan hal berikut:
/* 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)
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
. Membutuhkan Scalaz.
@switch
anotasi dalam Scala 2.8:
Anotasi untuk diterapkan pada ekspresi kecocokan. Jika ada, kompiler akan memverifikasi bahwa pertandingan telah dikompilasi ke tableswitch atau lookupswitch, dan mengeluarkan kesalahan jika bukan mengkompilasi ke dalam serangkaian ekspresi kondisional.
Contoh:
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 {
Tidak tahu apakah ini benar-benar tersembunyi, tetapi saya merasa cukup bagus.
Typeconstructors yang mengambil 2 tipe parameter dapat ditulis dalam notasi infix
object Main {
class FooBar[A, B]
def main(args: Array[String]): Unit = {
var x: FooBar[Int, BigInt] = null
var y: Int FooBar BigInt = null
}
}
var foo2barConverter: Foo ConvertTo Bar
akan membuat urutan parameter tipe menjadi jelas.
Scala 2.8 memperkenalkan argumen default dan bernama, yang memungkinkan penambahan metode "copy" baru yang ditambahkan Scala ke kelas kasus. Jika Anda mendefinisikan ini:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
dan Anda ingin membuat Foo baru yang seperti Foo yang ada, hanya dengan nilai "n" yang berbeda, maka Anda bisa mengatakan:
foo.copy(n = 3)
dalam scala 2.8 Anda dapat menambahkan @specialized ke kelas / metode umum Anda. Ini akan membuat versi khusus kelas untuk tipe primitif (memperluas AnyVal) dan menghemat biaya tinju / unboxing yang tidak perlu:
class Foo[@specialized T]...
Anda dapat memilih subset dari AnyVals:
class Foo[@specialized(Int,Boolean) T]...
Memperluas bahasa. Saya selalu ingin melakukan sesuatu seperti ini di Jawa (tidak bisa). Tetapi dalam Scala saya dapat memiliki:
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
}
dan kemudian menulis:
val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed { // "timed" is a new "keyword"!
numbers.sortWith(_<_)
}
println(sorted)
dan dapatkan
Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
Anda dapat menetapkan parameter panggilan-dengan-nama (EDITED: ini berbeda dari parameter malas!) Ke suatu fungsi dan itu tidak akan dievaluasi sampai digunakan oleh fungsi (EDIT: pada kenyataannya, itu akan dievaluasi ulang setiap kali itu bekas). Lihat faq ini untuk detailnya
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
*/
lazy val xx: Bar = x
dalam metode Anda dan sejak saat itu Anda hanya menggunakan xx
.
Anda dapat menggunakan locally
untuk memperkenalkan blok lokal tanpa menyebabkan masalah inferensi titik koma.
Pemakaian:
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
didefinisikan dalam "Predef.scala" sebagai:
@inline def locally[T](x: T): T = x
Menjadi sejajar, itu tidak membebankan biaya tambahan apa pun.
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)
Keluaran:
In AbstractT2:
AbstractT2: value = 10, inverse = 0.1
c2c.value = 10, inverse = 0.1
Kami membuat instance kelas dalam anonim, menginisialisasi
value
bidang di blok, sebelumwith AbstractT2
klausa. Ini menjamin yangvalue
diinisialisasi sebelum tubuhAbstractT2
dijalankan, seperti yang ditunjukkan ketika Anda menjalankan skrip.
Anda dapat membuat tipe struktural dengan kata kunci 'with'
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
}
}
sintaksis placeholder untuk fungsi anonim
Dari Spesifikasi Bahasa Scala:
SimpleExpr1 ::= '_'
Ekspresi (dari kategori sintaksis
Expr
) dapat berisi simbol garis bawah yang disematkan_
di tempat-tempat di mana pengidentifikasi legal. Ungkapan seperti itu mewakili fungsi anonim di mana kemunculan selanjutnya dari garis bawah menunjukkan parameter yang berurutan.
Dari Perubahan Bahasa 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)
Dengan ini Anda bisa melakukan sesuatu seperti:
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
Definisi tersirat, terutama konversi.
Misalnya, asumsikan fungsi yang akan memformat string input agar sesuai dengan ukuran, dengan mengganti bagian tengahnya dengan "...":
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
}
Anda dapat menggunakannya dengan String apa pun, dan, tentu saja, gunakan metode toString untuk mengonversi apa saja. Tapi Anda juga bisa menulisnya seperti ini:
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
}
Dan kemudian, Anda bisa lulus kelas tipe lain dengan melakukan ini:
implicit def double2String(d: Double) = d.toString
Sekarang Anda dapat memanggil fungsi itu lewat ganda:
sizeBoundedString(12345.12345D, 8)
Argumen terakhir adalah implisit, dan diajukan secara otomatis karena deklarasi implisit de. Selanjutnya, "s" diperlakukan seperti sebuah String di dalam sizeBoundedString karena ada konversi implisit dari itu ke String.
Implikasi dari tipe ini didefinisikan lebih baik untuk tipe yang tidak umum untuk menghindari konversi yang tidak terduga. Anda juga dapat secara eksplisit melewatkan konversi, dan itu masih akan digunakan secara implisit di dalam sizeBoundedString:
sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)
Anda juga dapat memiliki beberapa argumen implisit, tetapi kemudian Anda harus melewati semuanya, atau tidak melewati salah satunya. Ada juga sintaks pintasan untuk konversi implisit:
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
}
Ini digunakan dengan cara yang persis sama.
Implikasi dapat memiliki nilai apa pun. Mereka dapat digunakan, misalnya, untuk menyembunyikan informasi perpustakaan. Ambil contoh berikut, misalnya:
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")
}
}
Dalam contoh ini, memanggil "f" dalam objek Y akan mengirim log ke daemon default, dan pada instance X ke daemon X Daemon. Tetapi memanggil g pada instance X akan mengirim log ke DefaultDaemon yang diberikan secara eksplisit.
Sementara contoh sederhana ini dapat ditulis ulang dengan overload dan private state, implisit tidak memerlukan private state, dan dapat dibawa ke dalam konteks dengan impor.
Mungkin tidak terlalu tersembunyi, tapi saya pikir ini berguna:
@scala.reflect.BeanProperty
var firstName:String = _
Ini akan secara otomatis menghasilkan pengambil dan penyetel untuk bidang yang cocok dengan konvensi kacang.
Penjelasan lebih lanjut di developerworks
Argumen implisit dalam penutupan.
Argumen fungsi dapat ditandai sebagai implisit seperti halnya metode. Dalam ruang lingkup fungsi fungsi parameter implisit terlihat dan memenuhi syarat untuk resolusi implisit:
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")
})
}
Bangun struktur data tanpa batas dengan Scala's Stream
:
http://www.codecommit.com/blog/scala/infinite-lists-for-the-fininite-patient
Jenis hasil tergantung pada resolusi implisit. Ini dapat memberi Anda bentuk pengiriman ganda:
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
penggunaan dan a
yang harus ada di lingkungan sebelum pelaksanaan perintah-perintah ini. Saya menganggap Anda maksud z.perform(x)
.
Scala memungkinkan Anda untuk membuat subclass anonim dengan tubuh kelas (konstruktor) yang berisi pernyataan untuk menginisialisasi instance kelas itu.
Pola ini sangat berguna ketika membangun antarmuka pengguna berbasis komponen (misalnya Swing, Vaadin) karena memungkinkan untuk membuat komponen UI dan mendeklarasikan propertinya lebih ringkas.
Lihat http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf untuk informasi lebih lanjut.
Berikut adalah contoh membuat tombol Vaadin:
val button = new Button("Click me"){
setWidth("20px")
setDescription("Click on this")
setIcon(new ThemeResource("icons/ok.png"))
}
import
pernyataanMisalkan Anda ingin menggunakan Logger
yang berisi a println
dan printerr
metode, tetapi Anda hanya ingin menggunakan yang untuk pesan kesalahan, dan menyimpan yang lama baik Predef.println
untuk keluaran standar. Anda bisa melakukan ini:
val logger = new Logger(...)
import logger.printerr
tetapi jika logger
juga mengandung dua belas metode lain yang ingin Anda impor dan gunakan, menjadi tidak nyaman untuk mendaftarnya. Anda bisa mencoba:
import logger.{println => donotuseprintlnt, _}
tetapi ini masih "mencemari" daftar anggota yang diimpor. Masukkan wildcard kuat über:
import logger.{println => _, _}
dan itu akan melakukan hal yang benar ™.
require
metode (didefinisikan dalam Predef
) yang memungkinkan Anda untuk mendefinisikan kendala fungsi tambahan yang akan diperiksa selama waktu berjalan. Bayangkan Anda sedang mengembangkan klien twitter lain dan Anda perlu membatasi panjang tweet hingga 140 simbol. Selain itu Anda tidak dapat memposting tweet kosong.
def post(tweet: String) = {
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
}
Sekarang memanggil pos dengan argumen panjang yang tidak sesuai akan menyebabkan pengecualian:
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)
Anda dapat menulis beberapa persyaratan atau bahkan menambahkan deskripsi ke masing-masing:
def post(tweet: String) = {
require(tweet.length > 0, "too short message")
require(tweet.length < 140, "too long message")
println(tweet)
}
Sekarang pengecualian bertele-tele:
scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:8)
Satu contoh lagi ada di sini .
Anda dapat melakukan tindakan setiap kali persyaratan gagal:
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
require
bukan kata yang dilindungi undang-undang. Ini hanyalah metode yang didefinisikan dalam Predef
.
Ciri-ciri dengan abstract override
metode adalah fitur dalam Scala yang tidak banyak diiklankan seperti halnya banyak lainnya. Maksud dari metode dengan abstract override
pengubah adalah untuk melakukan beberapa operasi dan mendelegasikan panggilan ke super
. Maka sifat-sifat ini harus dicampur dengan implementasi konkret dari abstract override
metode mereka .
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
Walaupun contoh saya benar-benar tidak lebih dari AOP orang miskin, saya menggunakan Ciri - ciri Stackable ini sesuai dengan keinginan saya untuk membangun contoh juru bahasa Scala dengan impor yang telah ditentukan sebelumnya, binding kustom, dan jalur kelas. The Stackable Traits memungkinkan untuk membuat pabrik saya sepanjang garis new InterpreterFactory with JsonLibs with LuceneLibs
dan kemudian memiliki impor berguna dan ruang lingkup varibles untuk pengguna script.