您的位置:首页 > 其它

Scala学习笔记-伴生对象于孤立对象

2016-05-11 09:09 519 查看
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$


伴生类的属性、方法被定义为对象相关的。

伴生对象通常被用在单例对象或工具方法的容器
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: