Scala2.10新特性之 Value Classes

Value Classes and Universal Traits

http://docs.scala-lang.org/overviews/core/value-classes.html html

簡介

在Scala中,值類是避免分配運行時對象(avoid allocating runtime objects)的新機制。這是經過定義一個AnyVal的子類來完成的。該特性在SIP-15中被提出。下面展現了一個簡單的值類定義:
class Wrapper(val underlying: Int) extends AnyVal

這個類有一個單一的、公共的val參數,它是底層的運行時表示(underlying runtime representation)。編譯的時候,它是Wrapper類型的,而運行時,它表示一個Int。一個值類能夠定義方法(defs),但不能定義屬性(vals、vars),也不能嵌套特質(traits)、類或對象: java

class Wrapper(val underlying: Int) extends AnyVal {
    def foo: Wrapper = new Wrapper(underlying * 19)
}
一個值類只能擴展普通特質,但自身不能被擴展(值類是final型的)。普通特質是指那些擴展自Any(trait XXX extends Any)、只有方法和成員變量、沒有初始化的特質。對於值類而言,普通特質容許基本的方法繼承,但他們會產生資源分配開銷。例如:
trait Printable extends Any {
    def print(): Unit = println(this)
}
class Wrapper(val underlying: Int) extends AnyVal with Printable

val w = new Wrapper(3)
w.print() // 實際上須要實例化一個Wrapper對象 

這個文檔的剩餘部分展現了一些用法示例,詳細說明什麼時候會進行資源分配,而什麼時候不會發生,而且用具體的例子說明值類的侷限。 編程

擴展方法

一種值類的用法,是將它們和隱式類(SIP-13)結合使用(for allocation-free extension methods 啥意思?)。使用一個隱式類提供一個更方便的語法來定義擴展方法,並且值類移除了運行時的開銷。一個很好的例子就是標準類庫中的RichInt。 RichInt用幾個方法擴展了Int類型。由於是值類,因此當使用RichInt的方法時不須要建立它的實例。
如下RichInt的代碼片斷,展現了它擴展Int從而容許表達式3.toHexString。
class RichInt(val self: Int) extends AnyVal {
    def toHexString: String = java.lang.Integer.toHexString(self)
}

在運行時,表達式3.toHexString會優化成至關於對靜態對象的方法調用(RichInt$.MODULE$.extension$toHexString(3)),而不是對新實例化的對象的方法調用。 數組

正確性

值類的另外一個用法是獲得一個類型安全的數據類型,並且沒有運行時資源分配的開銷。例如表明一個距離的數據類型代碼片斷看起來是這樣的:
class Meter(val value: Double) extends AnyVal {
    def +(m: Meter): Meter = new Meter(value + m.value)
}

兩個距離增長的代碼就像這樣: 安全

val x = new Meter(3.4)
val y = new Meter(4.3)
val z = x + y

運行時,並無真的建立兩個Meter的實例,而是用原生的double類型。 注意:在實踐中,你可使用case class和/或擴展方法來得到更簡潔的語法。 app

必要的資源分配

因爲JVM不支持值類,Scala有時候須要實例化值類。具體細節請查看SIP-15。 ide

分配摘要

在這些狀況下,值類會被實例化:
一、把值類當作另外一個類型。
二、用值類指定數組。

三、運行時類型檢查,好比模式匹配(pattern matching)。 函數

分配細節

每當值類被當作另外一個類型時(包括普通特質),值類必須被實例化。看一個例子,考慮一下Meter值類:
trait Distance extends Any
case class Meter(val value: Double) extends AnyVal with Distance
若是一個方法接受Distance類型的值,那麼它須要一個真正的Meter實例。在接下來的例子中,Meter類會被實例化。
def add(a: Distance, b: Distance): Distance = ...
add(Meter(3.4), Meter(4.3))
若是add方法簽名用下面的代替:
def add(a: Meter, b: Meter): Meter = ...
那麼資源分配就不是必需的。這個規則的另外一個實例是,當值類被用做類型參數。例如,調用identity時,必需建立Meter的實例:
def identity[T](t: T): T = t
identity(Meter(5.0))
另外一個資源分配的狀況是指定一個數組,即便它是這個值類的數組。例如:
val m = Meter(5.0)
val array = Array[Meter](m)
這個數組包含實際的Meter實例,而非原生類型double。
最後,類型檢查(好比模式匹配或asInstanceOf)必需實例化值類:
case class P(val i: Int) extends AnyVal
val p = new P(3)
p match { // new P instantiated here
    case P(3) => println("Matched 3")
    case P(x) => println("Not 3")
}

侷限性

目前值類有幾個侷限,很大程度上是由於JVM沒有原生支持值類的概念。值類的實現詳情和它們的侷限性,請看SIP-15。 工具

侷限性摘要

一個值類...
一、只有一個主構造器,只有一個公共的val參數,而且類型不能是值類。
二、不能有專門的類型參數。
三、不能有嵌套或本地類,特質或對象。
四、不能定義equals或hashCode方法。
五、必需是頂級類或一個頂級可訪問對象的成員。
六、只能有成員方法。尤爲不能有lazy val,var或val做爲成員。

七、不能擴展另外一個類。 優化

侷限性的例子

這節提供了不少關於這些缺陷的具體影響,它們在「必要的資源分配」節沒有描述。

不容許多個構造器參數:
class Complex(val real: Double, val imag: Double) extends AnyVal
Scala編譯器會生成以下的錯誤消息:
Complex.scala:1: error: value class needs to have exactly one public val parameter
class Complex(val real: Double, val imag: Double) extends AnyVal
      ^
因爲構造器參數必須是val,因此不能是傳名參數(by-name parameter,Scala編程一書中翻譯爲傳名參數):
NoByName.scala:1: error: `val' parameters may not be call-by-name
class NoByName(val x: => Int) extends AnyVal
                      ^

Scala不容許延遲初始化的構造器參數,因此值類也不支持。也不容許多構造器。
(按個人理解,一個無參函數字面量()=>Int,去掉()就是傳名函數,因而就能夠用「1+2」做爲入參,來代替「()=>{1+2}」這種噁心的寫法)

class Secondary(val x: Int) extends AnyVal {
    def this(y: Double) = this(y.toInt)
}
Secondary.scala:2: error: value class may not have secondary constructors
    def this(y: Double) = this(y.toInt)
        ^
值類不能有lazy val或val的成員,也不能有嵌套類,特質,或對象。
class NoLazyMember(val evaluate: () => Double) extends AnyVal {
    val member: Int = 3
    lazy val x: Double = evaluate()
    object NestedObject
    class NestedClass
}

Invalid.scala:2: error: this statement is not allowed in value class: private[this] val member: Int = 3
    val member: Int = 3
        ^
Invalid.scala:3: error: this statement is not allowed in value class: lazy private[this] var x: Double = NoLazyMember.this.evaluate.apply()
    lazy val x: Double = evaluate()
             ^
Invalid.scala:4: error: value class may not have nested module definitions
    object NestedObject
           ^
Invalid.scala:5: error: value class may not have nested class definitions
    class NestedClass
          ^
注意本地類,特質和對象也是不容許的:
class NoLocalTemplates(val x: Int) extends AnyVal {
    def aMethod = {
        class Local
        ...
    }
}
當前的實現約束是,值類不能被嵌套:
class Outer(val inner: Inner) extends AnyVal
class Inner(val value: Int) extends AnyVal

Nested.scala:1: error: value class may not wrap another user-defined value class
class Outer(val inner: Inner) extends AnyVal
                ^
此外,不能在結構類型(structural types)的方法參數或返回類型中使用值類:
class Value(val x: Int) extends AnyVal
object Usage {
    def anyValue(v: { def value: Value }): Value =
        v.value
}

Struct.scala:3: error: Result type in structural refinement may not refer to a user-defined value class
    def anyValue(v: { def value: Value }): Value =
                                 ^
值類不能擴展非普通特質(non-universal trait),值類也不能被擴展:
trait NotUniversal
class Value(val x: Int) extends AnyVal with notUniversal
class Extend(x: Int) extends Value(x)

Extend.scala:2: error: illegal inheritance; superclass AnyVal
    is not a subclass of the superclass Object
    of the mixin trait NotUniversal
class Value(val x: Int) extends AnyVal with NotUniversal
                                            ^
Extend.scala:3: error: illegal inheritance from final class Value
class Extend(x: Int) extends Value(x)
                             ^
第二個錯誤信息代表,儘管值類沒有顯示聲明final修飾符,但它是假定存在的。
另外一個限制是...(中間這一大段是啥意思,各類什麼什麼從句要命啊)值類必須是頂級類或可訪問的靜態對象的成員。
(Another limitation that is a result of supporting only one parameter to a class is that a value class must be top-level or a member of a statically accessible object.)
class Outer {
    class Inner(val x: Int) extends AnyVal
}

Outer.scala:2: error: value class may not be a member of another class
class Inner(val x: Int) extends AnyVal
      ^
可是下面的例子是容許的,由於它被頂級對象包圍:
object Outer {
    class Inner(val x: Int) extends AnyVal
}

(好了,就到這吧)

2013-1-8 到家8點多了,寫了一下子年會節目的段子,而後想找幾個輔助翻譯的工具,都不太給力,仍是用網頁翻譯和詞典吧。快11點了,該睡覺了,才翻譯完《簡介》部分,要是按這個速度,估計我看完一本英文原版書的話得須要一年,呵呵。 2013-1-9 上六天班了,還有兩天,堅持堅持。今天整了一小段《擴展方法》,翻譯這活真累,之後就一小段一小段翻譯吧。 2013-1-24 中間隔了好長時間,中間有一週晚上到家就玩遊戲,而後又去看sbt。今天趁公司沒事,偷摸把剩下的弄完。剩下的那些好長啊,還有好多看不懂的,哎。
相關文章
相關標籤/搜索