DummyImplicits ....这个用过??? 如何?

Abi*_*oso 10 scala

在思考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
}
Run Code Online (Sandbox Code Playgroud)

无论如何都有一个线索为什么这片看似无用的代码存在?

Rob*_*ick 26

归根结底,它归结为类型擦除(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")
}
Run Code Online (Sandbox Code Playgroud)

这里一切都很好.但是如果我们将参数从单个值更改为序列呢?

object Foo { 
   def foo(ps: String*) = 1
   def foo(ps: Int*) = 2
   def foo(ps: Any*) = 3
} 

object Main extends App {
    Foo.foo("1")
}
Run Code Online (Sandbox Code Playgroud)

现在我们有一个错误:

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
Run Code Online (Sandbox Code Playgroud)

并且看到消息"擦除后具有相同类型" - 这是我们的线索.

为什么序列失败了?

因为JVM不支持泛型 - 这意味着您的强类型集合(如int或字符串序列)实际上不是.它们被编译为Object的容器,因为这是JVM所期望的.这些类型被"删除".

因此,在编译之后,所有内容都看起来像这样(我们将在一瞬间看到它们到底是什么):

object Foo { 
   def foo(ps: Object*) = 1
   def foo(ps: Object*) = 2
   def foo(ps: Object*) = 3
} 
Run Code Online (Sandbox Code Playgroud)

显然这不是预期的.

那么Java如何处理这个呢?

它在幕后创建了Bridge Method.魔法!

Scala没有做那种魔术(尽管已经讨论) - 而是使用虚拟暗示.

让我们改变我们的定义

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 
} 
Run Code Online (Sandbox Code Playgroud)

现在它编译!但为什么?

让我们看一下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>();
      ()
    }
  };
Run Code Online (Sandbox Code Playgroud)

好的 - 所以我们有三种不同的foo方法,它们的隐含参数不同.

现在我们打电话给他们:

object Main extends App {
    Foo.foo("1")
    Foo.foo(1)
    Foo.foo(1.0)
}
Run Code Online (Sandbox Code Playgroud)

那是什么样的(我在这里删除了很多其他代码......)

  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());
Run Code Online (Sandbox Code Playgroud)

因此,每次调用都给出了正确消除调用歧义所需的隐式参数.

那为什么DummyImplicit存在?确保存在一个始终存在隐含值的类型(否则您需要确保它可用).

它的文档说明"一种总有隐含价值的类型". - 所以隐含值总是存在于像这样的情况下使用.

  • 你完美地解释了它们最常见的用法,但具有讽刺意味的是你没有提到它似乎被引入的原因,正如在"DummyImplicit"的评论中暗示的那样,它暗示了它在`Array.fallbackCanBuildFrom`中的使用.在那里,它用于控制隐式查找的顺序.另外,据我所知,Java桥接方法的目的不是解决与此处讨论的相同的问题. (8认同)