我刚刚尝试了下面的代码,它按预期工作。它打印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)
}
答案 0 :(得分: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);
}
显式伴随对象定义导致的生成的伴随对象的差异似乎是:
如果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(否则它们是私有的),我们必须将工厂方法添加到我们的伴随对象,或者在创建实例时使用'new'关键字A(1,2)。