是否使用了 DummyImplicits,如果使用,如何使用? [英] Are DummyImplicits used, and if so, how?

查看:62
本文介绍了是否使用了 DummyImplicits,如果使用,如何使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在琢磨Predef.scala的代码时,我注意到以下几点:

While pondering through the code of Predef.scala I noticed the following:

/** 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
}

有没有人知道这段看似无用的代码为什么存在?

Does anyone have a clue why this piece of seemingly useless code exists?

推荐答案

最终归结为 输入擦除(Java和Scala都使用).

Ultimately it comes down to type erasure (which both Java and Scala use).

想象一下这段代码:

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

object Main extends App {
    Foo.foo("1")
}

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

Everything is good here. But what if we change the parameters from individual values to a sequence?

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

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

And see the message "have same type after erasure" - that's our clue.

那么为什么序列失败了?

So why did the sequence fail?

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

Because the JVM does not support generics - this means that your strongly types collections (like the sequence of ints or strings), really aren't. They are compiled to containers of Object because that's what the JVM expects. The types are "erased".

所以在编译这些之后看起来像这样(我们稍后会看到它们到底是什么):

So after compiling these all look something like this (we'll see exactly what they are in a moment):

object Foo { 
   def foo(ps: Object*) = 1
   def foo(ps: Object*) = 2
   def foo(ps: Object*) = 3
} 

显然这不是我们想要的.

Obviously that is not what was intended.

Java 如何处理这个问题?

So how does Java deal with this?

它在幕后创建了桥接方法.魔法!

It creates Bridge Methods behind the scenes. Magic!

Scala 没有这种魔法(尽管它已经讨论) - 而是使用虚拟隐式.

Scala doesn't do that magic (though it has been discussed) - rather it uses dummy implicits.

让我们改变我们的定义

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 
} 

现在它编译了!但是……为什么?

And now it compiles! But ... why?

我们看一下scalac生成的代码(scalac -print foo.scala)

Let's look at the code that scalac generated (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 方法,它们的区别仅在于它们的隐式参数.

OK - so we have three distinct foo methods that differ only by their implicit parameters.

现在让我们称他们为:

object Main extends App {
    Foo.foo("1")
    Foo.foo(1)
    Foo.foo(1.0)
}

那看起来像什么(我在这里删除了很多其他代码......)

And what does that look like (I'm removing a LOT of other code here ...)

  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());

因此,每个调用都被赋予了正确消除调用歧义所需的隐式参数.

So each call was given the implicit parameter needed to properly disambiguate the call.

那么为什么 DummyImplicit 会存在呢?确保有一个类型总是有一个隐式值(否则你需要确保它可用).

So why does DummyImplicit exists? To make sure there is a type for which there will always be an implicit value (otherwise you'd need to make sure it was available).

它的文档说明一种总是有隐含值的类型."- 所以在这种情况下总是存在隐式值.

It's documentation states "A type for which there is always an implicit value." - so the implicit value always exists to be used in cases like this.

这篇关于是否使用了 DummyImplicits,如果使用,如何使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆