Các tính năng ẩn của Scala mà mọi nhà phát triển Scala nên biết là gì?
Xin vui lòng một tính năng ẩn cho mỗi câu trả lời.
Các tính năng ẩn của Scala mà mọi nhà phát triển Scala nên biết là gì?
Xin vui lòng một tính năng ẩn cho mỗi câu trả lời.
Câu trả lời:
Được rồi, tôi phải thêm một cái nữa. Mọi Regex
đối tượng trong Scala đều có trình trích xuất (xem câu trả lời từ oxbox_lakes ở trên) cho phép bạn truy cập vào các nhóm khớp. Vì vậy, bạn có thể làm một cái gì đó như:
// 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"
Dòng thứ hai có vẻ khó hiểu nếu bạn không quen sử dụng so khớp mẫu và trích xuất. Bất cứ khi nào bạn xác định một val
hoặc var
, những gì xuất hiện sau từ khóa không chỉ đơn giản là một định danh mà là một mẫu. Đó là lý do tại sao điều này hoạt động:
val (a, b, c) = (1, 3.14159, "Hello, world")
Biểu thức bàn tay phải tạo ra một Tuple3[Int, Double, String]
mẫu có thể khớp với mẫu (a, b, c)
.
Hầu hết thời gian các mẫu của bạn sử dụng các trình trích xuất là thành viên của các đối tượng singleton. Ví dụ: nếu bạn viết một mẫu như
Some(value)
sau đó bạn đang ngầm gọi trình trích xuất Some.unapply
.
Nhưng bạn cũng có thể sử dụng các thể hiện lớp trong các mẫu và đó là những gì đang xảy ra ở đây. Val regex là một ví dụ Regex
và khi bạn sử dụng nó trong một mẫu, bạn đang gọi ngầm regex.unapplySeq
( unapply
so với unapplySeq
phạm vi của câu trả lời này), trích xuất các nhóm khớp thành một Seq[String]
, các yếu tố được gán để các biến số năm, tháng và ngày.
Định nghĩa kiểu cấu trúc - tức là một kiểu được mô tả bởi phương thức nào nó hỗ trợ. Ví dụ:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
Lưu ý rằng loại tham số closeable
không được xác định khác với close
phương thức
Nếu không có tính năng này, bạn có thể, ví dụ, thể hiện ý tưởng ánh xạ một chức năng qua một danh sách để trả về một danh sách khác hoặc ánh xạ một chức năng qua một cây để trả về một cây khác. Nhưng bạn không thể bày tỏ ý tưởng này nói chung mà không có loại cao hơn.
Với các loại cao hơn, bạn có thể nắm bắt ý tưởng về bất kỳ loại nào được tham số hóa với loại khác. Một constructor loại có một tham số được gọi là loại (*->*)
. Ví dụ , List
. Một hàm tạo kiểu trả về hàm tạo kiểu khác được gọi là loại (*->*->*)
. Ví dụ , Function1
. Nhưng trong Scala, chúng ta có các loại cao hơn , vì vậy chúng ta có thể có các hàm tạo kiểu được tham số hóa với các hàm tạo kiểu khác. Vì vậy, họ thuộc loại như ((*->*)->*)
.
Ví dụ:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
Bây giờ, nếu bạn có một Functor[List]
, bạn có thể ánh xạ qua danh sách. Nếu bạn có một Functor[Tree]
, bạn có thể lập bản đồ trên cây. Nhưng quan trọng hơn, nếu bạn có Functor[A]
bất kỳ loại A nào(*->*)
, bạn có thể ánh xạ một hàm qua A
.
Trình trích xuất cho phép bạn thay thế if-elseif-else
mã kiểu lộn xộn bằng các mẫu. Tôi biết rằng những thứ này không được giấu chính xác nhưng tôi đã sử dụng Scala trong một vài tháng mà không thực sự hiểu sức mạnh của chúng. Ví dụ (dài) tôi có thể thay thế:
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)
}
Với điều này, rõ ràng hơn nhiều theo ý kiến của tôi
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
Tôi phải làm một chút công việc trong nền ...
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
}
}
Nhưng các công việc đáng giá vì thực tế là nó tách một phần logic kinh doanh thành một nơi hợp lý. Tôi có thể thực hiện Product.getCode
các phương pháp của mình như sau ..
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
Các biểu hiện là một cách để có được thông tin loại trong thời gian chạy, như thể Scala có các loại thống nhất.
Trong scala 2.8, bạn có thể có các phương thức đệ quy đuôi bằng cách sử dụng gói scala.util.control.TailCalls (trên thực tế, đó là trampolining).
Một ví dụ:
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)
Các lớp trường hợp tự động trộn vào đặc điểm của Sản phẩm, cung cấp quyền truy cập được lập chỉ mục, được lập chỉ mục vào các trường mà không có bất kỳ phản ánh nào:
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)
Tính năng này cũng cung cấp một cách đơn giản hóa để thay đổi đầu ra của toString
phương thức:
case class Person(name: String, age: Int) {
override def productPrefix = "person: "
}
// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28))
Nó không được ẩn chính xác, nhưng chắc chắn là một tính năng được quảng cáo: scalac -Xprint .
Như một minh họa về việc sử dụng, hãy xem xét các nguồn sau:
class A { "xx".r }
Biên dịch cái này với scalac -Xprint: typer output:
package <empty> {
class A extends java.lang.Object with ScalaObject {
def this(): A = {
A.super.this();
()
};
scala.this.Predef.augmentString("xx").r
}
}
Lưu ý scala.this.Predef.augmentString("xx").r
, đó là một ứng dụng của implicit def augmentString
hiện tại trong Predef.scala.
scalac -Xprint: <phase> sẽ in cây cú pháp sau một số pha biên dịch. Để xem các pha có sẵn, hãy sử dụng các giai đoạn -Xshow-scalac .
Đây là một cách tuyệt vời để tìm hiểu những gì đang diễn ra đằng sau hậu trường.
Thử với
case class X(a:Int,b:String)
sử dụng giai đoạn typer để thực sự cảm thấy nó hữu ích như thế nào.
Bạn có thể xác định cấu trúc điều khiển của riêng bạn. Nó thực sự chỉ là các chức năng và đối tượng và một số đường cú pháp, nhưng chúng trông và hành xử như thật.
Ví dụ: đoạn mã sau định nghĩa dont {...} unless (cond)
và 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
}
}
Bây giờ bạn có thể làm như sau:
/* 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
. Yêu cầu Scalaz.
@switch
chú thích trong Scala 2.8:
Một chú thích được áp dụng cho một biểu thức khớp. Nếu có, trình biên dịch sẽ xác minh rằng trận đấu đã được biên dịch thành một bảng biểu hoặc tra cứu và đưa ra lỗi nếu thay vào đó nó biên dịch thành một chuỗi các biểu thức điều kiện.
Thí dụ:
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 {
Nếu điều này thực sự bị che giấu, nhưng tôi thấy nó khá hay.
Máy đánh chữ có 2 tham số loại có thể được viết bằng ký hiệu 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
sẽ làm cho thứ tự của các tham số loại tự hiển nhiên.
Scala 2.8 đã giới thiệu các đối số mặc định và được đặt tên, điều này có thể bổ sung một phương thức "sao chép" mới mà Scala thêm vào các lớp tình huống. Nếu bạn xác định điều này:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
và bạn muốn tạo một Foo mới giống như một Foo hiện có, chỉ với một giá trị "n" khác, thì bạn chỉ có thể nói:
foo.copy(n = 3)
trong scala 2.8, bạn có thể thêm @specialized vào các lớp / phương thức chung của mình. Điều này sẽ tạo các phiên bản đặc biệt của lớp cho các loại nguyên thủy (mở rộng AnyVal) và tiết kiệm chi phí cho việc đóng hộp / bỏ hộp không cần thiết:
class Foo[@specialized T]...
Bạn có thể chọn một tập hợp con của AnyVals:
class Foo[@specialized(Int,Boolean) T]...
Mở rộng ngôn ngữ. Tôi luôn muốn làm một cái gì đó như thế này trong Java (không thể). Nhưng ở Scala tôi có thể có:
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
}
và sau đó viết:
val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed { // "timed" is a new "keyword"!
numbers.sortWith(_<_)
}
println(sorted)
và lấy
Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
Bạn có thể chỉ định một tham số gọi theo tên (EDITED: đây là một tham số lười biếng!) Cho một hàm và nó sẽ không được đánh giá cho đến khi được sử dụng bởi hàm (EDIT: trên thực tế, nó sẽ được đánh giá lại mỗi khi nó được đã sử dụng). Xem faq này để biết chi tiết
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
trong phương pháp của bạn và từ lúc đó bạn chỉ sử dụng xx
.
Bạn có thể sử dụng locally
để giới thiệu một khối cục bộ mà không gây ra các vấn đề suy luận dấu chấm phẩy.
Sử dụng:
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
được định nghĩa trong "Predef.scala" là:
@inline def locally[T](x: T): T = x
Là nội tuyến, nó không áp đặt bất kỳ chi phí bổ sung.
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)
Đầu ra:
In AbstractT2:
AbstractT2: value = 10, inverse = 0.1
c2c.value = 10, inverse = 0.1
Chúng tôi khởi tạo một lớp bên trong ẩn danh, khởi tạo
value
trường trong khối, trướcwith AbstractT2
mệnh đề. Điều này đảm bảovalue
được khởi tạo trước khi phần thân củaAbstractT2
được thực thi, như được hiển thị khi bạn chạy tập lệnh.
Bạn có thể soạn các loại cấu trúc với từ khóa 'với'
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
}
}
cú pháp giữ chỗ cho các hàm ẩn danh
Từ Đặc tả ngôn ngữ Scala:
SimpleExpr1 ::= '_'
Một biểu thức (thuộc danh mục cú pháp
Expr
) có thể chứa các ký hiệu gạch dưới được nhúng_
tại các vị trí nơi định danh là hợp pháp. Một biểu thức như vậy biểu thị một hàm ẩn danh trong đó các lần xuất hiện tiếp theo của dấu gạch dưới biểu thị các tham số liên tiếp.
_ + 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)
Sử dụng cái này bạn có thể làm một cái gì đó như:
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
Định nghĩa ngầm, đặc biệt là chuyển đổi.
Ví dụ: giả sử một hàm sẽ định dạng chuỗi đầu vào phù hợp với kích thước, bằng cách thay thế giữa của nó bằng "...":
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
}
Bạn có thể sử dụng điều đó với bất kỳ Chuỗi nào và, tất nhiên, sử dụng phương thức toString để chuyển đổi mọi thứ. Nhưng bạn cũng có thể viết nó như thế này:
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
}
Và sau đó, bạn có thể vượt qua các lớp loại khác bằng cách thực hiện điều này:
implicit def double2String(d: Double) = d.toString
Bây giờ bạn có thể gọi hàm đó đi qua một đôi:
sizeBoundedString(12345.12345D, 8)
Đối số cuối cùng là ẩn và được truyền tự động vì khai báo ẩn. Hơn nữa, "s" đang được xử lý như một Chuỗi bên trong sizeBoundedString vì có một chuyển đổi ngầm định từ nó sang Chuỗi.
Ý nghĩa của loại này được xác định tốt hơn cho các loại không phổ biến để tránh chuyển đổi không mong muốn. Bạn cũng có thể vượt qua một chuyển đổi một cách rõ ràng và nó vẫn sẽ được sử dụng hoàn toàn bên trong sizeBoundedString:
sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)
Bạn cũng có thể có nhiều đối số ngầm, nhưng sau đó bạn phải vượt qua tất cả chúng, hoặc không vượt qua bất kỳ đối số nào. Ngoài ra còn có một cú pháp phím tắt cho chuyển đổi ngầm:
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
}
Điều này được sử dụng chính xác theo cùng một cách.
Implicits có thể có bất kỳ giá trị. Chúng có thể được sử dụng, ví dụ, để ẩn thông tin thư viện. Lấy ví dụ sau đây, ví dụ:
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")
}
}
Trong ví dụ này, việc gọi "f" trong một đối tượng Y sẽ gửi nhật ký đến daemon mặc định và trên một thể hiện của X đến daemon X daemon. Nhưng việc gọi g trên một thể hiện của X sẽ gửi nhật ký đến DefaultDaemon được cung cấp rõ ràng.
Trong khi ví dụ đơn giản này có thể được viết lại với tình trạng quá tải và trạng thái riêng tư, các ẩn ý không yêu cầu trạng thái riêng tư và có thể được đưa vào ngữ cảnh với nhập khẩu.
Có thể không quá ẩn, nhưng tôi nghĩ điều này hữu ích:
@scala.reflect.BeanProperty
var firstName:String = _
Điều này sẽ tự động tạo ra một getter và setter cho trường khớp với quy ước bean.
Mô tả thêm tại developerworks
Lập luận ngầm trong đóng cửa.
Một đối số hàm có thể được đánh dấu là ẩn giống như với các phương thức. Trong phạm vi phần thân của hàm, tham số ẩn được hiển thị và đủ điều kiện để phân giải ngầm:
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")
})
}
Xây dựng cấu trúc dữ liệu vô hạn với Scala's Stream
:
http://www.codecommit.com/blog/scala/infinite-lists-for-the-finite-patient
Các loại kết quả phụ thuộc vào độ phân giải ngầm. Điều này có thể cung cấp cho bạn một hình thức của nhiều công văn:
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
sử dụng một a
cái phải có trong môi trường trước khi thực hiện các lệnh này. Tôi giả sử bạn có nghĩa là z.perform(x)
.
Scala cho phép bạn tạo một lớp con ẩn danh với phần thân của lớp (hàm tạo) có chứa các câu lệnh để khởi tạo thể hiện của lớp đó.
Mẫu này rất hữu ích khi xây dựng giao diện người dùng dựa trên thành phần (ví dụ: Swing, Vaadin) vì nó cho phép tạo các thành phần UI và khai báo các thuộc tính của chúng chính xác hơn.
Xem http://spot.colorado.edu/~reids/ con / how-scala-experience-improved-our-java-developer-re-2011.pdf để biết thêm thông tin.
Dưới đây là một ví dụ về việc tạo nút Vaadin:
val button = new Button("Click me"){
setWidth("20px")
setDescription("Click on this")
setIcon(new ThemeResource("icons/ok.png"))
}
import
báo cáoGiả sử bạn muốn sử dụng một phương thức Logger
chứa a println
và một printerr
phương thức, nhưng bạn chỉ muốn sử dụng một phương thức cho các thông báo lỗi và giữ cũ tốt Predef.println
cho đầu ra tiêu chuẩn. Bạn có thể làm điều này:
val logger = new Logger(...)
import logger.printerr
nhưng nếu logger
cũng chứa mười hai phương thức khác mà bạn muốn nhập và sử dụng, việc liệt kê chúng trở nên bất tiện. Thay vào đó, bạn có thể thử:
import logger.{println => donotuseprintlnt, _}
nhưng điều này vẫn "gây ô nhiễm" danh sách các thành viên nhập khẩu. Nhập ký tự đại diện mạnh mẽ über:
import logger.{println => _, _}
và điều đó sẽ làm đúng việc ™.
require
phương thức (được xác định trong Predef
) cho phép bạn xác định các ràng buộc chức năng bổ sung sẽ được kiểm tra trong thời gian chạy. Hãy tưởng tượng rằng bạn đang phát triển một ứng dụng twitter khác và bạn cần giới hạn độ dài tweet lên tới 140 ký hiệu. Hơn nữa, bạn không thể đăng tweet trống.
def post(tweet: String) = {
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
}
Bây giờ gọi bài với đối số độ dài không phù hợp sẽ gây ra ngoại lệ:
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)
Bạn có thể viết nhiều yêu cầu hoặc thậm chí thêm mô tả cho từng yêu cầu:
def post(tweet: String) = {
require(tweet.length > 0, "too short message")
require(tweet.length < 140, "too long message")
println(tweet)
}
Bây giờ các ngoại lệ là dài dòng:
scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:8)
Một ví dụ nữa là ở đây .
Bạn có thể thực hiện một hành động mỗi khi yêu cầu không thành công:
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
không phải là một từ dành riêng. Đó là nhưng một phương pháp được định nghĩa trong Predef
.
Đặc điểm với abstract override
các phương pháp là một tính năng trong Scala không được quảng cáo rộng rãi như nhiều người khác. Mục đích của các phương thức với công cụ abstract override
sửa đổi là thực hiện một số thao tác và ủy thác cuộc gọi đến super
. Sau đó, những đặc điểm này phải được trộn lẫn với việc thực hiện cụ thể các abstract override
phương pháp của họ .
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
Mặc dù ví dụ của tôi thực sự không khác gì một người nghèo AOP, tôi đã sử dụng các Đặc điểm có thể xếp chồng này theo ý thích của mình để xây dựng các phiên bản trình thông dịch Scala với nhập khẩu được xác định trước, các ràng buộc tùy chỉnh và đường dẫn lớp. Các đặc điểm có thể xếp chồng đã cho phép tạo nhà máy của tôi dọc theo các dòng new InterpreterFactory with JsonLibs with LuceneLibs
và sau đó có các biến nhập phạm vi và phạm vi hữu ích cho các tập lệnh của người dùng.