scala - 是否使用了 DummyImplicits,如果使用,如何使用?

标签 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
}
有没有人知道为什么会存在这段看似无用的代码?

最佳答案

最终归结为 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 不支持泛型 - 这意味着您的强类型集合(如整数或字符串序列)实际上不支持。它们被编译为 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 会存在呢?确保有一个类型总是有一个隐式值(否则你需要确保它可用)。

它的文档指出“一种总是有隐含值的类型”。 - 所以在这种情况下总是存在隐式值。

关于scala - 是否使用了 DummyImplicits,如果使用,如何使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34745066/

相关文章:

java - scala 宏注释的注释是什么?或者宏应用了多少次

scala - 不可变对象(immutable对象)的缺点

Java Goose 不在 Android 上提取内容

scala - 以ORC格式保存spark rdd

scala - 捕捉Scala future 中未处理的错误

scala - 与scala反射库不一致

scala - Spark 集群无法从远程 Scala 应用程序分配资源

scala - Collection View 与 withFilter

git - 我应该将 jar 依赖项放在共享存储库中还是只放在源文件中?

java - Java排序和Scala排序有性能比较吗?