Scala2.10新特性之 Value Classes

Value Classes and Universal Traits

http://docs.scala-lang.org/overviews/core/value-classes.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)、类或对象:

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和/或扩展方法来获得更简洁的语法。

必要的资源分配

由于JVM不支持值类,Scala有时候需要实例化值类。具体细节请查看SIP-15。

分配摘要

在这些情况下,值类会被实例化:
1、把值类当做另一个类型。
2、用值类指定数组。

3、运行时类型检查,比如模式匹配(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。

局限性摘要

一个值类...
1、只有一个主构造器,只有一个公共的val参数,并且类型不能是值类。
2、不能有专门的类型参数。
3、不能有嵌套或本地类,特质或对象。
4、不能定义equals或hashCode方法。
5、必需是顶级类或一个顶级可访问对象的成员。
6、只能有成员方法。尤其不能有lazy val,var或val作为成员。

7、不能扩展另一个类。

局限性的例子

这节提供了很多关于这些缺陷的具体影响,它们在“必要的资源分配”节没有描述。

不允许多个构造器参数:
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。今天趁公司没事,偷摸把剩下的弄完。剩下的那些好长啊,还有好多看不懂的,哎。

转载于:https://my.oschina.net/u/580483/blog/104626

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值