scala – 为案例类合并自定义和编译器生成的伴随对象.什么是合并规则?

我刚刚尝试了下面的代码,它按预期工作.它打印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)
}

最佳答案 我不知道合并自动和显式伴随对象的算法在哪里被描述或记录(除了编译器源),但是通过编译代码然后检查生成的伴随对象(使用javap),我们可以看出差异是什么是(这是scala 2.10.4).

这是为case类生成的伴随对象(没有你的附加伴随对象):

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

显式伴随对象定义导致的生成的伴随对象的差异似乎是:

>它不再扩展AbstractFunction2
>它不再具有与子弹1相关的工厂方法(申请)
>它不再覆盖toString方法(我想如果需要,你应该提供一个)
>添加了您的A2Int方法

如果case类更改为普通类(以及进行编译所需的最小更改),结果如下:

  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中,我们需要向类A参数添加val(否则它们是私有的),我们必须将工厂方法添加到我们的伴随对象,或者在创建A的实例时使用’new’关键字(1 ,2).

点赞