标签:
Scala-伴生对象于孤立对象
Scala虽然是基于JVM构建的,但与Java还是有很多区别,其实一个重要的区别是Scala比Java更面向对象(纯面向对象),最明显的用例是scala中所有的操作符都是方法(操作符面向的使用者是对象)。
伴生对象/孤立对象也是scala作为纯面向对象语言的一种体现。
先看一个例子
object Test{
var a = "helloworld"
def helloworld(): Unit = {
println("helloworld")
}
}
使用时,它给人的感觉类似下面的代码
public class Test {
public static String a = "helloworld";
public static void helloworld() {
System.out.println("helloworld");
}
}
所以,我们可以向下面的方式访问a
字段或helloworld()
方法
Test.a
Test.helloworld()
这会给人以上的错误,但实际情况并非如此,此处的Test实际上是一个对象(全局单例),看下面的例子
object Test{
var a = "helloworld"
def helloworld(): Unit = {
println("helloworld")
}
def main(args: Array[String]) {
println(Test)
}
}
在REPL中运行结果如下:
D:\> scalac Test.scala
D:\> scala Test
Test$@3eb25e1a
D:\>
到此,总结一下,对于object Test{...}
,我们在使用Test.field
或Test.method
时,Test
实际上是一个全局单例的对象,而不一个类,下面通过反编译的class文件看一下具体细节
scala源码
object Test{
var a = "helloworld"
def helloworld(): Unit = {
println("helloworld")
}
}
编译后的Java代码
虚构类:
import scala.Predef.;
public final class Test$
{
public static final MODULE$;
private String a;
static
{
new ();
}
public String a()
{
return this.a; }
public void a_$eq(String x$1) { this.a = x$1; }
public void helloworld() {
Predef..MODULE$.println("helloworld");
}
private Test$() { MODULE$ = this;
this.a = "helloworld";
}
}
伴生类
public final class Test
{
public static void helloworld()
{
Test..MODULE$.helloworld();
}
public static void a_$eq(String paramString)
{
Test..MODULE$.a_$eq(paramString);
}
public static String a()
{
return Test..MODULE$.a();
}
}
从上面有缺陷的反编译代码中我们可以看出,在Test上的所有调用最后都作用到单例对象MODULE$
上,整个逻辑机构如下
调入入口(Test.xxxx)
↓
伴生类(static方法)
↓
虚构类(单例对象MODULE$)
带有伴生类的孤立对象叫做伴生对象,伴生类与伴生对象有如下关系:
伴生类源码与伴生对象源码必须在同一个.scala
文件中
伴生类与伴生对象名称必须相同
下面为孤立对象Test创建一个伴生类
import scala.beans.BeanProperty
class Test{
@BeanProperty var name = "zhangsan"
def func1() {
println("func1")
}
}
object Test{
var a = "helloworld"
def helloworld(): Unit = {
println("helloworld")
}
}
有了前面的基础,直接分析编译后的源码
虚构类:
import scala.Predef.;
public final class Test$
{
public static final MODULE$;
private String a;
static
{
new ();
}
public String a()
{
return this.a; }
public void a_$eq(String x$1) { this.a = x$1; }
public void helloworld() {
Predef..MODULE$.println("helloworld");
}
private Test$() { MODULE$ = this;
this.a = "helloworld";
}
}
伴生类:
public class Test
{
private String name = "zhangsan";
public static void helloworld() { Test..MODULE$.helloworld(); }
public static void a_$eq(String paramString) { Test..MODULE$.a_$eq(paramString); }
public static String a() { return Test..MODULE$.a(); }
public String name() { return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
public void setName(String x$1) { this.name = x$1; }
public void func1() {
Predef..MODULE$.println("func1");
}
public String getName()
{
return name();
}
}
从上述反编译后的代码来看,对于同时具有伴生类和伴生对象的scala代码,编译后,仍然是两个class文件,在伴生对象中的属性和方法仍然按照前面的规则被编译,但在伴生类中的属性和方法有所不同,它被放到伴生类的class文件中,并且是非静态的,这就意味着这些属性和方法是实例相关的,这是符合预期的。
那么,对于Test来说
val test1 = new Test
val test2 = new Test
test1 != test2
对象test1
与test2
是不同的示例,但是
Test.equals(Test)
Test == Test
都为true,因为此处Test被指向了全局唯一的单例对象MODULE$
。
总结一下
伴生对象的属性、方法仍指向全局单例对象MODULE$
伴生类的属性、方法被定义为对象相关的。
伴生对象通常被用在单例对象或工具方法的容器
标签:
原文地址:http://blog.csdn.net/u012302681/article/details/51354343