4

我刚刚在下面尝试了这段代码,它按预期工作。它打印1

现在,我的问题是我不明白引擎盖下发生了什么。

案例类如何有两个伴随对象(一个由编译器生成,一个由我编写)?可能它不能。所以它们必须在引擎盖下以某种方式合并。我只是不明白它们是如何合并的?是否有任何我应该注意的特殊合并规则?

是不是这样,如果两个伴随对象中定义的定义集是不相交的,那么结果案例类中的定义集只是两个不相交集的并集?我认为这就是它们的合并方式,但我不确定。有人可以确认这个合并规则是否是在 Scala 编译器中实现的吗?还是有额外的东西?

更具体地说,编译器生成的伴随对象和我的伴随对象合并的规则是什么?这些规则是否在某处指定?

我还没有真正看到在我读过的几本 Scala 书籍中讨论过这个主题——也许太肤浅了——阅读。

object A{
  implicit def A2Int(a:A)=a.i1
}
case class A(i1:Int,i2:Int)

object Run extends App{
  val a=A(1,2)
  val i:Int=a
  println(i)
}
4

1 回答 1

2

我不知道在哪里描述或记录了用于合并自动和显式伴随对象的算法(编译器源除外),但是通过编译代码然后检查生成的伴随对象(使用 javap),我们可以看到有什么区别是(这是 scala 2.10.4)。

这是为案例类生成的伴随对象(没有额外的伴随对象):

  Compiled from "zip.sc"
  public final class A$ extends scala.runtime.AbstractFunction2<Object, Object, A> implements scala.Serializable {
    public static final A$ MODULE$;
    public static {};
    public A apply(int, int);
    public scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(A);

    public java.lang.Object apply(java.lang.Object, java.lang.Object);
    public final java.lang.String toString();
  }

添加伴生对象后,生成的内容如下:

  Compiled from "zip.sc"
  public final class A$ implements scala.Serializable {
    public static final A$ MODULE$;
    public static {};
    public A apply(int, int);
    public scala.Option<scala.Tuple2<java.lang.Object, java.lang.Object>> unapply(A);
    public int A2Int(A);
  }

由显式伴随对象定义引起的生成伴随对象的差异似乎是:

  1. 它不再扩展 AbstractFunction2
  2. 它不再具有与项目符号 1 相关的工厂方法(应用)
  3. 它不再覆盖 toString 方法(如果需要,我想您应该提供一个)
  4. 您的 A2Int 方法已添加

如果将案例类更改为普通类(以及使其编译所需的最小更改),结果如下:

  Compiled from "zip.sc"
  public final class A$ {
    public static final A$ MODULE$;
    public static {};
    public A apply(int, int);
    public int A2Int(A);
  }

所以看起来如果你声明自己的伴生对象,至少在这个简单的例子中,效果是你的新方法被添加到伴生对象中,并且它的一些实现和功能也丢失了。如果我们试图覆盖一些剩余的自动生成的东西,看看会发生什么会很有趣,但是剩下的东西不多了,所以一般来说不太可能引起冲突。

案例类的一些好处与生成的代码无关,例如无需显式添加“val”关键字即可公开类变量。这是上面所有 3 个反编译示例的修改后的源代码。

版本 1(没有明确的伴随对象):

case class A(i1:Int,i2:Int)

版本 2 是您的原始版本。

版本 3(无案例类):

object A {
  implicit def A2Int(a:A)=a.i1
  def apply(a:Int,b:Int):A = new A(a,b)
}
class A(val i1:Int,val i2:Int)

object Run extends App{
  import A._
  val a=A(1,2)
  val i:Int=a
}

在版本 3 中,我们需要将 val 添加到 A 类参数(否则它们是私有的),我们必须将工厂方法添加到我们的伴随对象,或者在创建 A(1 ,2)。

于 2014-04-22T01:10:39.833 回答