在思考Predef.scala
的代码时,我注意到以下内容:
/** A type for which there is always an implicit value.
* @see [[scala.Array$]], method `fallbackCanBuildFrom`
*/
class DummyImplicit
object DummyImplicit {
/** An implicit value yielding a `DummyImplicit`.
* @see [[scala.Array$]], method `fallbackCanBuildFrom`
*/
implicit def dummyImplicit: DummyImplicit = new DummyImplicit
}
无论如何都知道为什么这段看似无用的代码存在?
答案 0 :(得分:22)
最终归结为type erasure(Java和Scala都使用它)。
想象一下这段代码:
object Foo {
def foo(p: String) = 1
def foo(p: Int) = 2
def foo(p: Any) = 3
}
object Main extends App {
Foo.foo("1")
}
这里的一切都很好。但是如果我们将参数从单个值更改为序列呢?
object Foo {
def foo(ps: String*) = 1
def foo(ps: Int*) = 2
def foo(ps: Any*) = 3
}
object Main extends App {
Foo.foo("1")
}
现在我们遇到了错误:
Main.scala:4: error: double definition:
def foo(ps: Int*): Int at line 3 and
def foo(ps: Any*): Int at line 4
have same type after erasure: (ps: Seq)Int
def foo(ps: Any*) = 3
^
Main.scala:3: error: double definition:
def foo(ps: String*): Int at line 2 and
def foo(ps: Int*): Int at line 3
have same type after erasure: (ps: Seq)Int
def foo(ps: Int*) = 2
^
two errors found
看到消息"擦除后有相同的类型" - 这是我们的线索。
那为什么序列会失败?
因为JVM不支持泛型 - 这意味着您的强类型集合(如int或字符串序列)确实不是。它们被编译为Object的容器,因为它是JVM所期望的。这些类型已被删除"。
因此,在编译之后,所有内容都看起来像这样(我们将在短时间内看到它们究竟是什么):
object Foo {
def foo(ps: Object*) = 1
def foo(ps: Object*) = 2
def foo(ps: Object*) = 3
}
显然这不符合预期。
那么Java如何解决这个问题?
它在幕后创建Bridge Methods。魔术!
Scala没有做那种魔法(尽管它已经discussed) - 而是使用虚假暗示。
让我们改变我们的定义
object Foo {
def foo(ps: String*) = 1
def foo(ps: Int*)(implicit i: DummyImplicit) = 2
def foo(ps: Any*)(implicit i1: DummyImplicit, i2: DummyImplicit) = 3
}
现在它编译!但是......为什么?
让我们看一下scalac生成的代码(scalac -print foo.scala)
object Foo extends Object {
def foo(ps: Seq): Int = 1;
def foo(ps: Seq, i: Predef$DummyImplicit): Int = 2;
def foo(ps: Seq, i1: Predef$DummyImplicit, i2: Predef$DummyImplicit): Int = 3;
def <init>(): Foo.type = {
Foo.super.<init>();
()
}
};
好的 - 所以我们有三个不同的foo方法,它们只有它们的隐含参数不同。
现在让我们打电话给他们:
object Main extends App {
Foo.foo("1")
Foo.foo(1)
Foo.foo(1.0)
}
这看起来是什么样的(我在这里删除了很多其他代码......)
Foo.foo(scala.this.Predef.wrapRefArray(Array[String]{"1"}.$asInstanceOf[Array[Object]]()));
Foo.foo(scala.this.Predef.wrapIntArray(Array[Int]{1}), scala.Predef$DummyImplicit.dummyImplicit());
Foo.foo(scala.this.Predef.genericWrapArray(Array[Object]{scala.Double.box(1.0)}), scala.Predef$DummyImplicit.dummyImplicit(), scala.Predef$DummyImplicit.dummyImplicit());
因此,每次调用都给出了正确消除调用歧义所需的隐式参数。
为什么DummyImplicit存在?确保存在一种始终存在隐含值的类型(否则您需要确保它可用)。
它的文档说明&#34;一种始终存在隐含价值的类型。&#34; - 所以隐含值总是存在于这样的情况下使用。