如何在 Scala 中获取类型的默认值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5260298/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I obtain the default value for a type in Scala?
提问by Jean-Philippe Pellet
I'm trying to write a Scala function that returns the default value of a type (0, 0.0, false, '\0', etc. for value types and null for reference types). I came up with this:
我正在尝试编写一个 Scala 函数,该函数返回类型的默认值(值类型为 0、0.0、false、'\0' 等,引用类型为 null)。我想出了这个:
def defaultValue[U]: U = {
class Default[U] { var default: U = _ }
new Default[U].default
}
and while this works well if called directly, it returns null even for value types when called through a function that itself is generic, as shown in this REPL session:
虽然这在直接调用时效果很好,但当通过本身是通用的函数调用时,即使对于值类型,它也会返回 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.
Can someone explain to me:
谁能给我解释一下:
- why this happens (and why the compiler/interpreter doesn't complain if there is not enough information for it to return a true Boolean); and
- how I can fix it?
- 为什么会发生这种情况(以及为什么编译器/解释器在没有足够的信息来返回真正的布尔值时不会抱怨);和
- 我该如何解决?
采纳答案by iain
You can create your own Defaulttype-classto handle this. Here is what the code looks like. I added special handling for scala collections that returns an empty collection instead of null.
您可以创建自己的Default类型类来处理此问题。下面是代码的样子。我为 scala 集合添加了特殊处理,它返回一个空集合而不是 null。
import scala.collection.immutable
class Default[+A](val default: A)
trait LowerPriorityImplicits {
// Stop AnyRefs from clashing 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
}
These are the results of using this in the repl. Notice that the default value for Stringcan be overriden by creating a new implicit Default[String].
这些是在 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] = Default@7d78d7b4
scala> Default.value[String]
res7: String = ""
回答by huynhjl
Here is a more condensed version of your issue:
这是您问题的更精简版本:
scala> defaultValue[Boolean]: Any
res0: Any = null
scala> defaultValue[Boolean]: Boolean
res1: Boolean = false
The first version is what applies when you call res = defaultValue[U]because even though Uis of type Boolean, resis of type Any
第一个版本适用于您调用时的情况,res = defaultValue[U]因为即使U是布尔类型,res也是Any类型
If you compile this little program using the -Xprint:alloption
如果您使用-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)
}
}
You'll see that right before the erasure phase, you have:
你会在擦除阶段之前看到,你有:
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)
Then at the end of the erasure phase:
然后在擦除阶段结束时:
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))
So what happens is that under the hood defaultValue[Boolean]returns null in both cases, but then null is unboxed into false when the return type is a Boolean. You can verify that in the REPL:
所以发生的情况是,defaultValue[Boolean]在这两种情况下,引擎盖下都返回 null,但是当返回类型是布尔值时,null 被拆箱为 false。您可以在 REPL 中验证:
scala> Boolean.unbox(null)
res0: Boolean = false
scala> null.asInstanceOf[Boolean]
res1: Boolean = false
Edit: I had an idea - not that I'm recommending it. Not sure what your use case is (res = falseseems easier to me..)
编辑:我有一个想法 - 不是我推荐它。不确定您的用例是什么(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
回答by Jean-Philippe Pellet
For the record, here's the only I've found (yet) to make this work reliably. Improvements are welcome.
作为记录,这是我发现(尚未)使这项工作可靠的唯一方法。欢迎改进。
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" => 'def defaultValue[U: ClassManifest]: U = new Array[U](1)(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]
}
I'm aware that I get null even if T <: NotNull, which is a problem. Then again, there is a problem with initialization of vars with _for NotNullsubclasses.
我知道即使 ,我也会得到 null T <: NotNull,这是一个问题。再说一次,使用_forNotNull子类初始化 var 存在问题。
回答by Sebastien Diot
I know there is already "best answer", but what about the really simple:
我知道已经有了“最佳答案”,但是真正简单的答案呢:
##代码##It seems to work, although it's somewhat expensive due to creating a temporary array object. Does anyone know of any cases where it gives the wrong value?
它似乎有效,尽管由于创建临时数组对象而有些昂贵。有谁知道它给出错误值的任何情况?
I was looking for a "cheaper" alternative, but this question tells me there probably isn't one.
我一直在寻找“更便宜”的替代方案,但这个问题告诉我可能没有。
回答by missingfaktor
I have written a blog post on building a defaulting mechanism for Scala. You can find it here.
我写了一篇关于为 Scala 构建默认机制的博客文章。你可以在这里找到它。
If you do not want Option[_]to default to None, Stringto ""etc then get rid of the respective implicits from the object Default.
如果您不想Option[_]默认为None,String要""等那么摆脱从对象各自implicits的Default。

