如何获得Scala中某个类型的默认值?

问题描述:

我想写一个Scala函数,返回一个类型的默认值(0,0.0,假,'\ 0'等值类型和null为参考类型)。我想出了这个:如何获得Scala中某个类型的默认值?

def defaultValue[U]: U = { 
    class Default[U] { var default: U = _ } 
    new Default[U].default 
} 

,虽然这工作得很好,如果直接调用,它甚至在通过函数本身是通用的所谓价值型返回null,如本REPL会话:

Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> def defaultValue[U]: U = { class Default[U] {var default: U = _ }; new Default[U].default } 
defaultValue: [U]U 

scala> defaultValue[Boolean] // direct call works 
res0: Boolean = false 

scala> var res: Any = 0 
res: Any = 0 

scala> def setRes[U] = { res = defaultValue[U]; defaultValue[U] } 
setRes: [U]U 

scala> setRes[Boolean] // returns a Boolean, but... 
res1: Boolean = false 

scala> res 
res2: Any = null // ... sets the res variable to null. 

有人能向我解释:

  1. 为什么发生这种情况(以及为什么如果没有足够的信息,它返回一个真正的布尔编译器/解释器不会抱怨);和
  2. 我该如何解决它?
+2

我发现一种方法可以用ClassManifests和匹配擦除,但它看起来不漂亮。 – 2011-03-10 14:33:24

+1

谁打赌的原因是不是类型擦除?无论如何,既然你只有许多已知的期望行为,为什么不硬编码呢?丑陋,是的,但可能是最有效的。 – Raphael 2011-03-10 19:10:30

您可以创建自己的Defaulttype-class来处理此问题。这是代码的样子。我为scala集合添加了特殊处理,它返回一个空集合而不是null。

import scala.collection.immutable 

class Default[+A](val default: A) 

trait LowerPriorityImplicits { 
    // Stop AnyRefs from *ing with AnyVals 
    implicit def defaultNull[A <: AnyRef]:Default[A] = new Default[A](null.asInstanceOf[A]) 
} 

object Default extends LowerPriorityImplicits { 
    implicit object DefaultDouble extends Default[Double](0.0) 
    implicit object DefaultFloat extends Default[Float](0.0F) 
    implicit object DefaultInt extends Default[Int](0) 
    implicit object DefaultLong extends Default[Long](0L) 
    implicit object DefaultShort extends Default[Short](0) 
    implicit object DefaultByte extends Default[Byte](0) 
    implicit object DefaultChar extends Default[Char]('\u0000') 
    implicit object DefaultBoolean extends Default[Boolean](false) 
    implicit object DefaultUnit extends Default[Unit](()) 

    implicit def defaultSeq[A]: Default[immutable.Seq[A]] = new Default[immutable.Seq[A]](immutable.Seq()) 
    implicit def defaultSet[A]: Default[Set[A]] = new Default[Set[A]](Set()) 
    implicit def defaultMap[A, B]: Default[Map[A, B]] = new Default[Map[A, B]](Map[A, B]()) 
    implicit def defaultOption[A]: Default[Option[A]] = new Default[Option[A]](None) 

    def value[A](implicit value: Default[A]): A = value.default 
} 

这些是在repl中使用它的结果。请注意,String的默认值可以通过创建新的隐含Default[String]来重写。

scala> Default.value[Int] 
res0: Int = 0 

scala> Default.value[Boolean] 
res1: Boolean = false 

scala> Default.value[String] 
res2: String = null 

scala> Default.value[Set[Int]] 
res3: Set[Int] = Set() 

scala> Default.value[immutable.Seq[Int]] 
res4: scala.collection.immutable.Seq[Int] = List() 

scala> Default.value[String] 
res5: String = null 

scala> Default.value[AnyRef] 
res6: AnyRef = null 

scala> implicit val emptyStringAsDefault:Default[String] = new Default[String]("") 
emptyStringAsDefault: Default[String] = [email protected] 

scala> Default.value[String] 
res7: String = "" 

这是你的问题更浓缩版本:

scala> defaultValue[Boolean]: Any 
res0: Any = null 

scala> defaultValue[Boolean]: Boolean 
res1: Boolean = false 

第一个版本是,当你调用res = defaultValue[U]因为即使U是布尔类型是什么应用,res类型的任何

如果使用-Xprint:all选项编译这个小程序

object Test { 
    def defaultValue[U]: U = { class Default[U] {var default: U = _ }; new Default[U].default } 

    def main(args:Array[String]) { 
    val any = defaultValue[Boolean]: Any 
    println(any) 
    val bool = defaultValue[Boolean]: Boolean 
    println(bool) 
    } 
} 

你会看到擦除阶段之前这项权利,你必须:

val any: Any = (Test.this.defaultValue[Boolean](): Any); 
scala.this.Predef.println(any); 
val bool: Boolean = (Test.this.defaultValue[Boolean](): Boolean); 
scala.this.Predef.println(bool) 

然后,在擦除阶段结束:

val any: java.lang.Object = (Test.this.defaultValue(): java.lang.Object); 
scala.this.Predef.println(any); 
val bool: Boolean = (scala.Boolean.unbox(Test.this.defaultValue()): Boolean); 
scala.this.Predef.println(scala.Boolean.box(bool)) 

所以会发生什么是引擎盖下defaultValue[Boolean]在这两种情况下都返回null,但是当返回类型是布尔值时,null将被拆箱为假。你可以验证在REPL:

scala> Boolean.unbox(null) 
res0: Boolean = false 

scala> null.asInstanceOf[Boolean] 
res1: Boolean = false 

编辑:我有一个想法 - 不是我推荐它。不知道你的使用情况是什么(res = false似乎更容易我..)

scala> def f[@specialized U] = { class X { var x: U = _ }; (new X).x } 
f: [U]U 

scala> var res: Any = _ 
res: Any = null 

scala> def g[@specialized U] = { res = f[U]; f[U] } 
g: [U]U 

scala> g[Boolean] 
res0: Boolean = false 

scala> res 
res1: Any = false 
+0

谢谢,这是为什么的一个非常好的解释。现在为什么编译器不能告诉我更多?喜欢,看看男人,你想要一个布尔值,但在这里我可能会给你null,对不起? – 2011-03-11 10:19:41

+0

不确定除了你拥有的'def f [T]:T'还有其他用例。没有任何传递参数很难实现T。所以可能没有警告,因为没有人想过这样做。 – huynhjl 2011-03-12 04:14:03

+0

@specialized技巧很好,但很危险,因为只要您将来自未专门化的上下文的调用包装为f,它就会崩溃。这可能会导致难以追查的错误。 – 2011-03-14 09:46:36

为了记录在案,这里是唯一的,我发现(还),使这项工作可靠。欢迎改进。

def defaultValue[T: ClassManifest]: T = classManifest[T].erasure.toString match { 
    case "void" =>().asInstanceOf[T] 
    case "boolean" => false.asInstanceOf[T] 
    case "byte" => (0: Byte).asInstanceOf[T] 
    case "short" => (0: Short).asInstanceOf[T] 
    case "char" => '\0'.asInstanceOf[T] 
    case "int" => 0.asInstanceOf[T] 
    case "long" => 0L.asInstanceOf[T] 
    case "float" => 0.0F.asInstanceOf[T] 
    case "double" => 0.0.asInstanceOf[T] 
    case _ => null.asInstanceOf[T] 
} 

我知道,我得到空就算T <: NotNull,这是一个问题。然后再次,对于NotNull子类,使用_进行变量初始化时出现问题。

我知道已经有“最佳答案”,但对于非常简单:

def defaultValue[U: ClassManifest]: U = new Array[U](1)(0) 

它似乎工作,虽然这是由于创建一个临时数组对象比较昂贵。有谁知道任何情况下它给出了错误的价值?

我正在寻找一个“便宜”的替代方案,但这个问题告诉我可能没有一个。

我写了一篇关于构建Scala缺省机制的博客文章。你可以找到它here

如果你不想Option[_]默认为NoneString""等则得到来自物体Default摆脱各自implicits的。