有時(shí)候,你并不需要指定一個(gè)類型是等/子/超于另一個(gè)類,你可以通過轉(zhuǎn)換這個(gè)類來偽裝這種關(guān)聯(lián)關(guān)系。一個(gè)視界指定一個(gè)類型可以被“看作是”另一個(gè)類型。這對(duì)對(duì)象的只讀操作是很有用的。
隱函數(shù)允許類型自動(dòng)轉(zhuǎn)換。更確切地說,在隱式函數(shù)可以幫助滿足類型推斷時(shí),它們?cè)试S按需的函數(shù)應(yīng)用。例如:
scala> implicit def strToInt(x: String) = x.toInt
strToInt: (x: String)Int
scala> "123"
res0: java.lang.String = 123
scala> val y: Int = "123"
y: Int = 123
scala> math.max("123", 111)
res1: Int = 123
視界,就像類型邊界,要求對(duì)給定的類型存在這樣一個(gè)函數(shù)。您可以使用<%
指定類型限制,例如:
scala> class Container[A <% Int] { def addIt(x: A) = 123 + x }
defined class Container
這是說 A 必須“可被視”為 Int 。讓我們?cè)囋嚒?/p>
scala> (new Container[String]).addIt("123")
res11: Int = 246
scala> (new Container[Int]).addIt(123)
res12: Int = 246
scala> (new Container[Float]).addIt(123.2F)
<console>:8: error: could not find implicit value for evidence parameter of type (Float) => Int
(new Container[Float]).addIt(123.2)
^
方法可以通過隱含參數(shù)執(zhí)行更復(fù)雜的類型限制。例如,List 支持對(duì)數(shù)字內(nèi)容執(zhí)行 sum,但對(duì)其他內(nèi)容卻不行??墒?Scala 的數(shù)字類型并不都共享一個(gè)超類,所以我們不能使用T <: Number
。相反,要使之能工作,Scala 的 math 庫對(duì)適當(dāng)?shù)念愋?T 定義了一個(gè)隱含的 Numeric[T]
。 然后在 List 定義中使用它:
sum[B >: A](implicit num: Numeric[B]): B
如果你調(diào)用List(1,2).sum()
,你并不需要傳入一個(gè) num 參數(shù);它是隱式設(shè)置的。但如果你調(diào)用 List("whoop").sum()
,它會(huì)抱怨無法設(shè)置 num。
在沒有設(shè)定陌生的對(duì)象為 Numeric 的時(shí)候,方法可能會(huì)要求某種特定類型的“證據(jù)”。這時(shí)可以使用以下類型-關(guān)系運(yùn)算符:
scala> class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value }
defined class Container
scala> (new Container(123)).addIt
res11: Int = 246
scala> (new Container("123")).addIt
<console>:10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int]
類似地,根據(jù)之前的隱式轉(zhuǎn)換,我們可以放松約束為可視性:
scala> class Container[A](value: A) { def addIt(implicit evidence: A <%< Int) = 123 + value }
defined class Container
scala> (new Container("123")).addIt
res15: Int = 246
在 Scala 標(biāo)準(zhǔn)庫中,視圖主要用于實(shí)現(xiàn)集合的通用函數(shù)。例如“min”函數(shù)(在 Seq[] 上)就使用了這種技術(shù):
def min[B >: A](implicit cmp: Ordering[B]): A = {
if (isEmpty)
throw new UnsupportedOperationException("empty.min")
reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
}
其主要優(yōu)點(diǎn)是:
scala> List(1,2,3,4).min
res0: Int = 1
scala> List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a })
res3: Int = 4
作為旁注,標(biāo)準(zhǔn)庫中有視圖來將 Ordered 轉(zhuǎn)換為 Ordering (反之亦然)。
trait LowPriorityOrderingImplicits {
implicit def ordered[A <: Ordered[A]]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A) = x.compare(y)
}
}
Scala2.8 引入了一種串聯(lián)和訪問隱式參數(shù)的快捷方式。
scala> def foo[A](implicit x: Ordered[A]) {}
foo: [A](implicit x: Ordered[A])Unit
scala> def foo[A : Ordered] {}
foo: [A](implicit evidence$1: Ordered[A])Unit
隱式值可能會(huì)通過 implicitly 被訪問
scala> implicitly[Ordering[Int]]
res37: Ordering[Int] = scala.math.Ordering$Int$@3a9291cf
相結(jié)合后往往會(huì)使用更少的代碼,尤其是串聯(lián)視圖的時(shí)候。
Scala 可以對(duì)“更高階”的類型進(jìn)行抽象。例如,假設(shè)您需要用幾種類型的容器處理幾種類型的數(shù)據(jù)。你可能定義了一個(gè) Container 的接口,它可以被實(shí)現(xiàn)為幾種類型的容器:Option、List 等。你要定義可以使用這些容器里的值的接口,但不想確定值的類型。
這類似與函數(shù)柯里化。例如,盡管“一元類型”有類似List[A]
的構(gòu)造函數(shù),這意味著我們必須滿足一個(gè)“級(jí)別”的類型變量來產(chǎn)生一個(gè)具體的類型(就像一個(gè)沒有柯里化的函數(shù)需要只提供一個(gè)參數(shù)列表來被調(diào)用),更高階的類型需要更多。
scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
scala> val container = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
container: java.lang.Object with Container[List] = $anon$1@7c8e3f75
scala> container.put("hey")
res24: List[java.lang.String] = List(hey)
scala> container.put(123)
res25: List[Int] = List(123)
注意:Container是參數(shù)化類型的多態(tài)(“容器類型”)。
如果我們結(jié)合隱式轉(zhuǎn)換 implicits 使用容器,我們會(huì)得到“特設(shè)的”多態(tài)性:即對(duì)容器寫泛型函數(shù)的能力。
scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
scala> implicit val listContainer = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
scala> implicit val optionContainer = new Container[Some] { def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get }
scala> def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = {
| val c = implicitly[Container[M]]
| c.put(c.get(fst), c.get(snd))
| }
tupleize: [M[_],A,B](fst: M[A],snd: M[B])(implicit evidence$1: Container[M])M[(A, B)]
scala> tupleize(Some(1), Some(2))
res33: Some[(Int, Int)] = Some((1,2))
scala> tupleize(List(1), List(2))
res34: List[(Int, Int)] = List((1,2))
通常有必要來訪問一個(gè)(泛型)特質(zhì)的具體子類。例如,想象你有一些泛型特質(zhì),但需要可以與它的某一子類進(jìn)行比較。
trait Container extends Ordered[Container]
然而,現(xiàn)在比較方法是必須的了
def compare(that: Container): Int
因此,我們不能訪問具體子類型,例如:
class MyContainer extends Container {
def compare(that: MyContainer): Int
}
編譯失敗,因?yàn)槲覀儗?duì) Container 指定了 Ordered 特質(zhì),而不是對(duì)特定子類型指定的。
為了調(diào)和這一點(diǎn),我們改用F-
界的多態(tài)性。
trait Container[A <: Container[A]] extends Ordered[A]
奇怪的類型!但可以看到怎樣對(duì) A 實(shí)現(xiàn)了 Ordered 參數(shù)化,它本身就是 Container[A]
所以,現(xiàn)在
class MyContainer extends Container[MyContainer] {
def compare(that: MyContainer) = 0
}
他們是有序的了:
scala> List(new MyContainer, new MyContainer, new MyContainer)
res3: List[MyContainer] = List(MyContainer@30f02a6d, MyContainer@67717334, MyContainer@49428ffa)
scala> List(new MyContainer, new MyContainer, new MyContainer).min
res4: MyContainer = MyContainer@33dfeb30
鑒于他們都是 Container[_]
的子類型,我們可以定義另一個(gè)子類并創(chuàng)建 Container[_]
的一個(gè)混合列表:
scala> class YourContainer extends Container[YourContainer] { def compare(that: YourContainer) = 0 }
defined class YourContainer
scala> List(new MyContainer, new MyContainer, new MyContainer, new YourContainer)
res2: List[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]]
= List(MyContainer@3be5d207, MyContainer@6d3fe849, MyContainer@7eab48a7, YourContainer@1f2f0ce9)
注意結(jié)果類型是怎樣成為 YourContainer
和 MyContainer
類型確定的下界。這是類型推斷的工作。有趣的是,這種類型甚至不需要是有意義的,它只是提供了一個(gè)合乎邏輯的最大下界為列表的統(tǒng)一類型。如果現(xiàn)在我們嘗試使用 Ordered 會(huì)發(fā)生什么?
(new MyContainer, new MyContainer, new MyContainer, new YourContainer).min
<console>:9: error: could not find implicit value for parameter cmp:
Ordering[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]]
對(duì)統(tǒng)一的類型 Ordered[]
不存在了。
Scala 支持結(jié)構(gòu)類型 structural types — 類型需求由接口構(gòu)造表示,而不是由具體的類型表示。
scala> def foo(x: { def get: Int }) = 123 + x.get
foo: (x: AnyRef{def get: Int})Int
scala> foo(new { def get = 10 })
res0: Int = 133
這可能在很多場(chǎng)景都是相當(dāng)不錯(cuò)的,但這個(gè)實(shí)現(xiàn)中使用了反射,所以要注意性能!
在特質(zhì)中,你可以讓類型成員保持抽象。
scala> trait Foo { type A; val x: A; def getX: A = x }
defined trait Foo
scala> (new Foo { type A = Int; val x = 123 }).getX
res3: Int = 123
scala> (new Foo { type A = String; val x = "hey" }).getX
res4: java.lang.String = hey
在做依賴注入等情況下,這往往是一個(gè)有用的技巧。
您可以使用 hash 操作符來引用一個(gè)抽象類型的變量:
scala> trait Foo[M[_]] { type t[A] = M[A] }
defined trait Foo
scala> val x: Foo[List]#t[Int] = List(1)
x: List[Int] = List(1)
正如我們所知道的,類型信息在編譯的時(shí)候會(huì)因?yàn)椴脸鴣G失。 Scala 的清單(Manifests)功能,使我們能夠選擇性地恢復(fù)類型信息。清單提供了一個(gè)隱含值,根據(jù)需要由編譯器生成。
scala> class MakeFoo[A](implicit manifest: Manifest[A]) { def make: A = manifest.erasure.newInstance.asInstanceOf[A] }
scala> (new MakeFoo[String]).make
res10: String = ""
參見: https://github.com/twitter/finagle
trait Service[-Req, +Rep] extends (Req => Future[Rep])
trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn]
extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])
{
def andThen[Req2, Rep2](next: Filter[ReqOut, RepIn, Req2, Rep2]) =
new Filter[ReqIn, RepOut, Req2, Rep2] {
def apply(request: ReqIn, service: Service[Req2, Rep2]) = {
Filter.this.apply(request, new Service[ReqOut, RepIn] {
def apply(request: ReqOut): Future[RepIn] = next(request, service)
override def release() = service.release()
override def isAvailable = service.isAvailable
})
}
}
def andThen(service: Service[ReqOut, RepIn]) = new Service[ReqIn, RepOut] {
private[this] val refcounted = new RefcountedService(service)
def apply(request: ReqIn) = Filter.this.apply(request, refcounted)
override def release() = refcounted.release()
override def isAvailable = refcounted.isAvailable
}
}
一個(gè)服務(wù)可以通過過濾器對(duì)請(qǐng)求進(jìn)行身份驗(yàn)證。
trait RequestWithCredentials extends Request {
def credentials: Credentials
}
class CredentialsFilter(credentialsParser: CredentialsParser)
extends Filter[Request, Response, RequestWithCredentials, Response]
{
def apply(request: Request, service: Service[RequestWithCredentials, Response]): Future[Response] = {
val requestWithCredentials = new RequestWrapper with RequestWithCredentials {
val underlying = request
val credentials = credentialsParser(request) getOrElse NullCredentials
}
service(requestWithCredentials)
}
}
注意底層服務(wù)是如何需要對(duì)請(qǐng)求進(jìn)行身份驗(yàn)證的,而且還是靜態(tài)驗(yàn)證。因此,過濾器可以被看作是服務(wù)轉(zhuǎn)換器。
許多過濾器可以被組合在一起:
val upFilter =
logTransaction andThen
handleExceptions andThen
extractCredentials andThen
homeUser andThen
authenticate andThen
route
享用安全的類型吧!
更多建議: