Scala学习笔记

特质(trait)

特质(scala)和接口(java)的异同点:

相同点:

  1. scala中的特质类似java中的接口(没有任何具体方法的时候),某个类可以实现多个特质。

不同点:

  1. 特质可以有默认方法实现
  2. 子类实现特质用extends关键字;实现接口用implements
  3. 实现多个特质时,中间用with连接;实现多个接口时,中间用,分割

scala版本

ConsoleLogger.scala
1
2
3
class ConsoleLogger extends Logger with Cloneable with Serializable {
}

java版本

ConsoleLogger.java
1
2
3
class ConsoleLogger implements Logger,Cloneable,Serializable {
}

JDK 8是否也有类似?


构造不可修改数据类

不可修改的数据类有各种好处:

  1. 不用担心线程安全
  2. 方便定位问题
  3. 对象一旦被创建,就不用担心数据被错误修改

Java语言实现不可变数据类主要有2种方法:

  1. 只有构造函数和getter的类
  2. 使用Builder模式

方法1的主要问题在于:如果类的成员变量有多个(例如大于3个),而且不是必选的话,需要重载多个构造函数,在使用的时候容易造成混乱。

方法2主要可参见《Effective Java 2nd Edition》的第2条

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
public class ImmutableObject {
private final int a; // required
private final int b; // required
private final int c; // optional
private final int d; // optional
private final int e; // optional
private ImmutableObject(Builder builder) {
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
this.d = builder.d;
this.e = builder.e;
}
@Override
public String toString() {
return "ImmutableObject{" +
"a=" + a +
", b=" + b +
", c=" + c +
", d=" + d +
", e=" + e +
'}';
}
public static class Builder {
private final int a;
private final int b;
// 可以设置默认值
private int c = 1;
private int d = 2;
private int e = 3;
public Builder(int a, int b) {
this.a = a;
this.b = b;
}
public Builder c(int c) {
this.c = c;
return this;
}
public Builder d(int d) {
this.d = d;
return this;
}
public Builder e(int e) {
this.e = e;
return this;
}
public ImmutableObject build() {
return new ImmutableObject(this);
}
}
public static void main(String[] args) {
ImmutableObject obj = new Builder(10, 20)
.c(30)
.d(40)
.build();
System.out.println(obj);
}
}

输出结果为:

1
ImmutableObject{a=10, b=20, c=30, d=40, e=3}

scala语言实现有3种方案

  1. Immutable Classes
  2. Case Classes
  3. Tuples

Immutable Class:将类的主构造函数中的参数都设为val,还可以设置参数的默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ImmutableScalaClass(
val a:Int,
val b:Int,
val c:Int = 1,
val d:Int = 2,
val e:Int = 3) {
override def toString = s"ImmutableScalaClass(a=$a, b=$b, c=$c, d=$d, e=$e)"
}
object ImmutableScalaObject {
def main(args: Array[String]): Unit = {
val obj = new ImmutableScalaClass(
a = 10,
b = 20,
c = 30,
d = 40
)
print(obj)
}
}

输出:

1
ImmutableScalaClass(a=10, b=20, c=30, d=40, e=3)

可以看出,scala的语法比java的Builder模式要清晰和直观很多

Case Classes: 将类设为case class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case class CaseScalaClass(
val a: Int,
val b: Int,
val c: Int = 1,
val d: Int = 2,
val e: Int = 3)
object CaseScalaObject {
def main(args: Array[String]): Unit = {
val obj = new CaseScalaClass(
a = 10,
b = 20,
c = 30,
d = 40
)
print(obj)
}
}

输出:

1
CaseScalaClass(10,20,30,40,3)

由于case class默认实现了toStringequalshashCode等方法,所以写法更简洁

Tuples

1
2
3
4
5
6
7
object TupleDemo {
def main(args: Array[String]): Unit = {
val t = (1, 2, 3, 4, 5)
print(t._1, t._2, t._3, t._4, t._5)
}
}

输出:

1
(1,2,3,4,5)

可以看出,Tuple不用创建新的类,使用简单,当然用途也很单一,单纯作为一个容器使用


主构造函数

函数与过程

函数:有返回值的方法,格式为:

1
2
3
def fuc(x: Int):Int = { // 有=号,返回值类型可以显示指定,不指定的话编译器也能推断出来(递归函数必须指定返回值类型)
......
}

过程:没有返回值(或者说返回值类型为Unit)的函数

1
2
3
4
5
6
7
8
9
def fuc(x: Int) { // 没有=号
......
}
或者
def fuc(x: Int):Unit = { // 有=号,但返回值类型为Unit
......
}

闭包

可以简单理解成:闭包就是用函数创建函数,创建时用到的参数或者外部变量,在结果函数中都会保存它们的引用

例子:scala实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object ClosureFunctionDemo {
def makeClosureFunction(x: Int, func: Int => String) = {
i: Int => func(x * i)
}
def baseFunction(i: Int): String = {
"This is string: " + i
}
def main(args: Array[String]): Unit = {
// 调用makeClosureFunction时,可以理解成将100和baseFunction两个参数作为closureFunction的成员变量存放起来
val closureFunction = makeClosureFunction(100, baseFunction)
// 调用closureFunction(10)时,利用成员变量100和baseFunction以及入参10进行计算,返回结果
print(closureFunction(10))
}
}

java实现

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
public class JavaClosureFunction {
public static void main(String[] args) {
/* JDK 7 mode
BaseFunction baseFunction = new BaseFunction() {
@Override
public String call(int i) {
return "This is string: " + i;
}
};
*/
// JDK 8 mode
BaseFunction baseFunction = i -> "This is string: " + i;
ClosureFunction closureFunction = makeClosureFunction(100, baseFunction);
System.out.println(closureFunction.call(10));
}
private static ClosureFunction makeClosureFunction(int i, BaseFunction baseFunction) {
/* JDK 7 mode
return new ClosureFunction() {
@Override
public String call(int x) {
return baseFunction.call(x * i);
}
};
*/
// JDK 8 mode
return x -> baseFunction.call(x * i);
}
}
public interface BaseFunction {
String call(int i);
}
public interface ClosureFunction {
String call(int i);
}