Scala之类定义

在scala中,类的声明有 trait, class, object 三种。其中trait声明接口,class为一般类的声明,object声明的类中的属性与方法都为静态,且在scala中没有static关键字。

接口的定义-trait

trait是接口类的声明,在scala中没有public关键字,因为没有指定为protectedprivate的对象或方法都是为public的,这一点与java不同。当trait中所有的方法都没有实现时,反编译其编译出的.class文件可知该类其实是一个abstract interface

1
2
3
trait Walking {
def walk(from: String, end: String): Unit
}

使用scalac Walking.scala编译成.class,通过jd-gui反编译.class出的内容为:

1
2
3
4
5
6
7
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001\0312q!\001\002\021\002G\005QAA\004XC2\\\027N\\4\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\rC\003\016\001\031\005a\"\001\003xC2\\GcA\b\023?A\021q\001E\005\003#!\021A!\0268ji\")1\003\004a\001)\005!aM]8n!\t)BD\004\002\0275A\021q\003C\007\0021)\021\021\004B\001\007yI|w\016\036 \n\005mA\021A\002)sK\022,g-\003\002\036=\t11\013\036:j]\036T!a\007\005\t\013\001b\001\031\001\013\002\007\025tG\rC\003#\001\031\0051%\001\003uC2\\GCA\b%\021\025)\023\0051\001\025\003\0259xN\0353t\001")
public abstract interface Walking
{
public abstract void walk(String paramString1, String paramString2);
}

trait声明的类中存在实现的方法时,其反编译的.class对象将会包含一个静态初始化方法:

1
2
3
4
5
6
7
trait Walking {
def walk(from: String, end: String): Unit

def talk(words: String): Unit = {
printf("talk %s.", words)
}
}

反编译内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import scala.Predef.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001%2q!\001\002\021\002\007\005QAA\004XC2\\\027N\\4\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\rC\003\016\001\021\005a\"\001\004%S:LG\017\n\013\002\037A\021q\001E\005\003#!\021A!\0268ji\")1\003\001D\001)\005!q/\0317l)\ryQC\t\005\006-I\001\raF\001\005MJ|W\016\005\002\031?9\021\021$\b\t\0035!i\021a\007\006\0039\021\ta\001\020:p_Rt\024B\001\020\t\003\031\001&/\0323fM&\021\001%\t\002\007'R\024\030N\\4\013\005yA\001\"B\022\023\001\0049\022aA3oI\")Q\005\001C\001M\005!A/\0317l)\tyq\005C\003)I\001\007q#A\003x_J$7\017")
public abstract interface Walking
{
public abstract void walk(String paramString1, String paramString2);

public void talk(String words)
{
Predef..MODULE$.printf("talk %s.", Predef..MODULE$.genericWrapArray(new Object[] { words }));
}

public static void $init$(Walking $this) {}
}

注意 这里在import语句中有import scala.Predef.;

tracit中包含属性或语句块时,其声明的属性getter/setter方法都会是abstract方法,有默认值则在静态初始方法$init$中赋值,trait中的语句块都在$init$静态方法中执行。

1
2
3
4
5
6
7
8
9
10
11
trait Walking {
def walk(from: String, end: String): Unit

var name: String = "hero"
val alias: String = "HERO"

println("this is a trait named Walking.")
println(name)
println(alias)
// alias = "NEW HERO" // 报错, val声明的属性不可变更, 即使在其反编译内容中含有setter方法(Walking$_setter_$alias_$eq)
}

其反编译结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import scala.Predef.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\00192q!\001\002\021\002\007\005QAA\004XC2\\\027N\\4\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\rC\003\016\001\021\005a\"\001\004%S:LG\017\n\013\002\037A\021q\001E\005\003#!\021A!\0268ji\")1\003\001D\001)\005!q/\0317l)\ryQC\t\005\006-I\001\raF\001\005MJ|W\016\005\002\031?9\021\021$\b\t\0035!i\021a\007\006\0039\021\ta\001\020:p_Rt\024B\001\020\t\003\031\001&/\0323fM&\021\001%\t\002\007'R\024\030N\\4\013\005yA\001\"B\022\023\001\0049\022aA3oI\"9Q\005\001a\001\n\0031\023\001\0028b[\026,\022a\006\005\bQ\001\001\r\021\"\001*\003!q\027-\\3`I\025\fHCA\b+\021\035Ys%!AA\002]\t1\001\037\0232\021\035i\003A1A\005\002\031\nQ!\0317jCN\004")
public abstract interface Walking
{
public abstract void Walking$_setter_$alias_$eq(String paramString);

public abstract void walk(String paramString1, String paramString2);

public abstract String name();

public abstract void name_$eq(String paramString);

public abstract String alias();

public static void $init$(Walking $this)
{
$this.name_$eq("hero");
$this.Walking$_setter_$alias_$eq("HERO");

Predef..MODULE$.println("this is a trait named Walking.");
Predef..MODULE$.println($this.name());
Predef..MODULE$.println($this.alias());
}
}

注意 以上实例在jdk8环境下使用jd-gui

类的定义-class

使用class声明类,实现trait声明的接口:

1
2
3
4
5
6
7
8
9
10
class MyWalking extends Walking {
override def walk(from: String, end: String): Unit = {
printf("walk from %s to %s.", from, end)
}

def test(): Unit = {
name = "NEW NAME"
printf("new name is %s, alias is %s.", name, alias)
}
}

MyWalking反编译结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import scala.Predef.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\00112A!\001\002\001\013\tIQ*_,bY.Lgn\032\006\002\007\0059A(Z7qift4\001A\n\004\001\031a\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\r\005\002\016\0355\t!!\003\002\020\005\t9q+\0317lS:<\007\"B\t\001\t\003\021\022A\002\037j]&$h\bF\001\024!\ti\001\001C\003\026\001\021\005c#\001\003xC2\\GcA\f\033OA\021q\001G\005\0033!\021A!\0268ji\")1\004\006a\0019\005!aM]8n!\tiBE\004\002\037EA\021q\004C\007\002A)\021\021\005B\001\007yI|w\016\036 \n\005\rB\021A\002)sK\022,g-\003\002&M\t11\013\036:j]\036T!a\t\005\t\013!\"\002\031\001\017\002\007\025tG\rC\003+\001\021\0051&\001\003uKN$H#A\f")
public class MyWalking
implements Walking
{
private String name;
private final String alias;

public void name_$eq(String x$1)
{
this.name = x$1;
}

public String alias()
{
return this.alias;
}

public void Walking$_setter_$alias_$eq(String x$1)
{
this.alias = x$1;
}

public MyWalking()
{
Walking.$init$(this);
}

public String name()
{
return this.name;
}

public void walk(String from, String end)
{
Predef..MODULE$.printf("walk from %s to %s.", Predef..MODULE$.genericWrapArray(new Object[] { from, end }));
}

public void test()
{
name_$eq("NEW NAME");
Predef..MODULE$.printf("new name is %s, alias is %s.", Predef..MODULE$.genericWrapArray(new Object[] { name(), alias() }));
}
}

可见Predef在scala中作用不凡

单例类-object

在scala中,没有静态修饰符,一般class声明的类需要通过对象实例操作类中的方法,通过object声明的类,就能通过类名直接调用类中的方法,这点与java语言中的static修饰符有类似的地方。只有通过object声明的类中的main方法才能被当做程序的入口。
object声明的类,都会具有一个对应的隐式类(虚构类),隐式类的类名即为在该类名后面加$,譬如:

1
2
3
4
5
6
7
8
9
10
11
12
object ObjectA {
var intVar: Int = 12
val intVal: Int = 23

def eat(food: String): Unit = {
println("eat " + food)
}

def main(args: Array[String]): Unit = {
eat("apple")
}
}

其反编译结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 反编译object类 ObjectA.class
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001}:Q!\001\002\t\002\025\tqa\0242kK\016$\030IC\001\004\003\035aT-\0349usz\032\001\001\005\002\007\0175\t!AB\003\t\005!\005\021BA\004PE*,7\r^!\024\005\035Q\001CA\006\017\033\005a!\"A\007\002\013M\034\027\r\\1\n\005=a!AB!osJ+g\rC\003\022\017\021\005!#\001\004=S:LGO\020\013\002\013!9Ac\002a\001\n\003)\022AB5oiZ\013'/F\001\027!\tYq#\003\002\031\031\t\031\021J\034;\t\017i9\001\031!C\0017\005Q\021N\034;WCJ|F%Z9\025\005qy\002CA\006\036\023\tqBB\001\003V]&$\bb\002\021\032\003\003\005\rAF\001\004q\022\n\004B\002\022\bA\003&a#A\004j]R4\026M\035\021\t\017\021:!\031!C\001+\0051\021N\034;WC2DaAJ\004!\002\0231\022aB5oiZ\013G\016\t\005\006Q\035!\t!K\001\004K\006$HC\001\017+\021\025Ys\0051\001-\003\0211wn\0343\021\0055\"dB\001\0303!\tyC\"D\0011\025\t\tD!\001\004=e>|GOP\005\003g1\ta\001\025:fI\0264\027BA\0337\005\031\031FO]5oO*\0211\007\004\005\006q\035!\t!O\001\005[\006Lg\016\006\002\035u!)1h\016a\001y\005!\021M]4t!\rYQ\bL\005\003}1\021Q!\021:sCf\004")
public final class ObjectA
{
public static void main(String[] paramArrayOfString)
{
ObjectA..MODULE$.main(paramArrayOfString);
}

public static void eat(String paramString)
{
ObjectA..MODULE$.eat(paramString);
}

public static int intVal()
{
return ObjectA..MODULE$.intVal();
}

public static void intVar_$eq(int paramInt)
{
ObjectA..MODULE$.intVar_$eq(paramInt);
}

public static int intVar()
{
return ObjectA..MODULE$.intVar();
}
}

// 其隐式Object类 Object$.class
import scala.Predef.;

public final class ObjectA$
{
public static MODULE$;
private int intVar;
private final int intVal;

public int intVar()
{
return this.intVar;
}

public void intVar_$eq(int x$1)
{
this.intVar = x$1;
}

public int intVal()
{
return this.intVal;
}

public void eat(String food)
{
Predef..MODULE$.println("eat " + food);
}

public void main(String[] args)
{
eat("apple");
}

private ObjectA$()
{
MODULE$ = this;this.intVar = 12;this.intVal = 23;
}

static
{
new ();
}
}

可以看出object声明的类是final的,故继承object是不能被继承的,其中声明的属性和方法都是static的,也就没有必要实例化一个object实例再去调用其中的方法,事实上object也不能使用new关键字去创建一个实例(trait也一样),即object声明的类是一个单例类object中的方法内容将在他的隐式类中体现并执行,它本身只是调用它隐式类中的同名方法。同时object类不含有构造方法,它的”构造方法”也将在隐式类中体现,因为object不提供构造方法,故object也不能带有参数,如果object的声明带有参数则会报错,譬如:

1
2
3
object ObjectA(name: String) {
... // 省略
}

其报错内容为
trait_object_may_not_have_parameters

伴生类
class声明一个类,再使用object声明一个与其名称一样的单例类,这两个类置于同一个.scala文件中,则这个类就成为这个单例类的伴生类,这个单例类也就是该类的伴生对象(单例)。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Man(name: String, age: Int) {
override def toString(): String = "name=" + name + ", age=" + age

def talk(words: String): Unit = {
printf("Man %s say: \"%s\" \n", name, words)
}
}

object Man {
def apply(name: String, age: Int): Man = new Man(name, age)

def apply(): Man = new Man("zero", 23)
}

其反编译内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Man.class
import scala.Predef.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001y2A!\001\002\001\013\t\031Q*\0318\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\r\003\005\016\001\t\005\t\025!\003\017\003\021q\027-\\3\021\005=1bB\001\t\025!\t\t\002\"D\001\023\025\t\031B!\001\004=e>|GOP\005\003+!\ta\001\025:fI\0264\027BA\f\031\005\031\031FO]5oO*\021Q\003\003\005\t5\001\021\t\021)A\0057\005\031\021mZ3\021\005\035a\022BA\017\t\005\rIe\016\036\005\006?\001!\t\001I\001\007y%t\027\016\036 \025\007\005\032C\005\005\002#\0015\t!\001C\003\016=\001\007a\002C\003\033=\001\0071\004C\003'\001\021\005s%\001\005u_N#(/\0338h)\005q\001\"B\025\001\t\003Q\023\001\002;bY.$\"a\013\030\021\005\035a\023BA\027\t\005\021)f.\033;\t\013=B\003\031\001\b\002\013]|'\017Z:\b\013E\022\001\022\001\032\002\0075\013g\016\005\002#g\031)\021A\001E\001iM\0211G\002\005\006?M\"\tA\016\013\002e!)\001h\rC\001s\005)\021\r\0359msR\031\021EO\036\t\01359\004\031\001\b\t\013i9\004\031A\016\t\013a\032D\021A\037\025\003\005\002")
public class Man
{
private final String name;
private final int age;

public static Man apply()
{
return Man..MODULE$.apply();
}

public static Man apply(String paramString, int paramInt)
{
return Man..MODULE$.apply(paramString, paramInt);
}

public String toString()
{
return "name=" + this.name + ", age=" + this.age;
}

public void talk(String words)
{
Predef..MODULE$.printf("Man %s say: \"%s\" \n", Predef..MODULE$.genericWrapArray(new Object[] { this.name, words }));
}

public Man(String name, int age) {}
}

// Man$.class
public final class Man$
{
public static MODULE$;

static
{
new ();
}

public Man apply(String name, int age)
{
return new Man(name, age);
}

public Man apply()
{
return new Man("zero", 23);
}

private Man$()
{
MODULE$ = this;
}
}

在使用具有伴生对象的类时

1
2
3
4
5
6
7
8
9
10
11
12
13
object TestMan {
def main(args: Array[String]): Unit = {
val man01 = Man("第一个男人", 23)
val man02 = new Man("第二个男人", 22)
val man03 = Man.apply()
val man04 = Man.apply("第四个男人", 12)
println(man01) // name=第一个男人, age=23
println(man02) // name=第二个男人, age=22
println(man03) // name=zero, age=23
println(man04) // name=第四个男人, age=12
man03.talk("Hello!") // Man zero say: "Hello!"
}
}

类的伴生对象中的属性和方法都是static的,故使用伴生对象的方式可以拓展类的功能

scala中的枚举

在scala中,是没有enum关键字的,那怎么去声明一个枚举类呢。通过object的介绍可知,object中的属性和方法都是static的(其实还是final的),同时,scala还提供Enumeration这个abstract class用来实现枚举类的声明,所以使用object继承Enumeration可以达到声明枚举类的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object ColorEnum extends Enumeration {
val RED, GREEN = Value
val YELLOW = Value(12)
val BLACK = Value("黑色")
val BLUE = Value(99, "蓝色")

def main(args: Array[String]): Unit = {
println(ColorEnum.RED) // RED
println(ColorEnum.GREEN) // GREEN
println(ColorEnum.YELLOW) // YELLOW
println(ColorEnum.BLACK) // 黑色
println(ColorEnum.BLUE) // 蓝色
}
}

其反编译内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// ColorEnum.class
import scala.Enumeration.Value;
import scala.Enumeration.ValueOrdering.;
import scala.Enumeration.ValueSet;
import scala.Enumeration.ValueSet.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001\t;Q!\001\002\t\002\025\t\021bQ8m_J,e.^7\013\003\r\tq\001P3naRLhh\001\001\021\005\0319Q\"\001\002\007\013!\021\001\022A\005\003\023\r{Gn\034:F]Vl7CA\004\013!\tYa\"D\001\r\025\005i\021!B:dC2\f\027BA\b\r\005-)e.^7fe\006$\030n\0348\t\013E9A\021\001\n\002\rqJg.\033;?)\005)\001b\002\013\b\005\004%\t!F\001\004%\026#U#\001\f\021\005]AR\"A\004\n\005eq!!\002,bYV,\007BB\016\bA\003%a#\001\003S\013\022\003\003bB\017\b\005\004%\t!F\001\006\017J+UI\024\005\007?\035\001\013\021\002\f\002\r\035\023V)\022(!\021\035\tsA1A\005\002U\ta!W#M\031>;\006BB\022\bA\003%a#A\004Z\0132cuj\026\021\t\017\025:!\031!C\001+\005)!\tT!D\027\"1qe\002Q\001\nY\taA\021'B\007.\003\003bB\025\b\005\004%\t!F\001\005\0052+V\t\003\004,\017\001\006IAF\001\006\0052+V\t\t\005\006[\035!\tAL\001\005[\006Lg\016\006\0020eA\0211\002M\005\003c1\021A!\0268ji\")1\007\fa\001i\005!\021M]4t!\rYQgN\005\003m1\021Q!\021:sCf\004\"\001O \017\005ej\004C\001\036\r\033\005Y$B\001\037\005\003\031a$o\\8u}%\021a\bD\001\007!J,G-\0324\n\005\001\013%AB*ue&twM\003\002?\031\001")
public final class ColorEnum
{
public static void main(String[] paramArrayOfString)
{
ColorEnum..MODULE$.main(paramArrayOfString);
}

public static Enumeration.Value BLUE()
{
return ColorEnum..MODULE$.BLUE();
}

public static Enumeration.Value BLACK()
{
return ColorEnum..MODULE$.BLACK();
}

public static Enumeration.Value YELLOW()
{
return ColorEnum..MODULE$.YELLOW();
}

public static Enumeration.Value GREEN()
{
return ColorEnum..MODULE$.GREEN();
}

public static Enumeration.Value RED()
{
return ColorEnum..MODULE$.RED();
}

public static Enumeration.ValueSet. ValueSet()
{
return ColorEnum..MODULE$.ValueSet();
}

public static Enumeration.ValueOrdering. ValueOrdering()
{
return ColorEnum..MODULE$.ValueOrdering();
}

public static Enumeration.Value withName(String paramString)
{
return ColorEnum..MODULE$.withName(paramString);
}

public static Enumeration.Value apply(int paramInt)
{
return ColorEnum..MODULE$.apply(paramInt);
}

public static int maxId()
{
return ColorEnum..MODULE$.maxId();
}

public static Enumeration.ValueSet values()
{
return ColorEnum..MODULE$.values();
}

public static String toString()
{
return ColorEnum..MODULE$.toString();
}
}

// ColorEnum$.class
import scala.Enumeration;
import scala.Enumeration.Value;
import scala.Predef.;

public final class ColorEnum$
extends Enumeration
{
public static MODULE$;
private final Enumeration.Value RED;
private final Enumeration.Value GREEN;
private final Enumeration.Value YELLOW;
private final Enumeration.Value BLACK;
private final Enumeration.Value BLUE;

public Enumeration.Value RED()
{
return this.RED;
}

public Enumeration.Value GREEN()
{
return this.GREEN;
}

public Enumeration.Value YELLOW()
{
return this.YELLOW;
}

public Enumeration.Value BLACK()
{
return this.BLACK;
}

public Enumeration.Value BLUE()
{
return this.BLUE;
}

private ColorEnum$()
{
MODULE$ = this;
this.RED = Value();this.GREEN = Value();
this.YELLOW = Value(12);
this.BLACK = Value("黑色");
this.BLUE = Value(99, "蓝色");
}

public void main(String[] args)
{
Predef..MODULE$.println(RED());
Predef..MODULE$.println(GREEN());
Predef..MODULE$.println(YELLOW());
Predef..MODULE$.println(BLACK());
Predef..MODULE$.println(BLUE());
}

static
{
new ();
}
}