本文记录 scala的一些比较 有意思的语法特性

提取器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import scala.util.Random

object CustomerID {

  def apply(name: String) = s"$name--${Random.nextLong}"

  def unapply(customerID: String): Option[String] = {
    val stringArray: Array[String] = customerID.split("--")
    if (stringArray.tail.nonEmpty) Some(stringArray.head) else None
  }
}

val customer1ID = CustomerID("Sukyoung")  // Sukyoung--23098234908
customer1ID match {
  case CustomerID(name) => println(name)  // prints Sukyoung
  case _ => println("Could not extract a CustomerID")
}

apply 就相当于构造器,接受参数然后创建一个对象实例
unapply接受一个实例对象,然后返回最初创建它所用的参数

翻编译看的话,就是两个 静态函数,下面是 CustomerID.class

 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
  public static scala.Option<java.lang.String> unapply(java.lang.String);
    descriptor: (Ljava/lang/String;)Lscala/Option;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #18                 // Field CustomerID$.MODULE$:LCustomerID$;
         3: aload_0
         4: invokevirtual #20                 // Method CustomerID$.unapply:(Ljava/lang/String;)Lscala/Option;
         7: areturn
    Signature: #11                          // (Ljava/lang/String;)Lscala/Option<Ljava/lang/String;>;
    MethodParameters:
      Name                           Flags
      customerID                     final

  public static java.lang.String apply(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #18                 // Field CustomerID$.MODULE$:LCustomerID$;
         3: aload_0
         4: invokevirtual #25                 // Method CustomerID$.apply:(Ljava/lang/String;)Ljava/lang/String;
         7: areturn
    MethodParameters:
      Name                           Flags
      name                           final

下面是 CustomerID$.class

 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
  public java.lang.String apply(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: new           #22                 // class java/lang/StringBuilder
         3: dup
         4: ldc           #23                 // int 2
         6: invokespecial #26                 // Method java/lang/StringBuilder."<init>":(I)V
         9: aload_1
        10: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/St
ringBuilder;
        13: ldc           #32                 // String --
        15: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/St
ringBuilder;
        18: getstatic     #37                 // Field scala/util/Random$.MODULE$:Lscala/util/Random$;
        21: invokevirtual #41                 // Method scala/util/Random$.nextLong:()J
        24: invokevirtual #44                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
        27: invokevirtual #48                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        30: areturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  this   LCustomerID$;
            0      31     1  name   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      name                           final

  public scala.Option<java.lang.String> unapply(java.lang.String);
    descriptor: (Ljava/lang/String;)Lscala/Option;
    flags: ACC_PUBLIC
    Code:
      stack=7, locals=3, args_size=2
         0: aload_1
         1: ldc           #32                 // String --
         3: invokevirtual #60                 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
         6: astore_2
         7: new           #7                  // class scala/collection/mutable/ArrayOps$ofRef
        10: dup
        11: getstatic     #65                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
        14: new           #7                  // class scala/collection/mutable/ArrayOps$ofRef
        17: dup
        18: getstatic     #65                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
        21: aload_2
        22: checkcast     #67                 // class "[Ljava/lang/Object;"
        25: invokevirtual #71                 // Method scala/Predef$.refArrayOps:([Ljava/lang/Object;)[Ljava/lang/Objec
t;
        28: invokespecial #74                 // Method scala/collection/mutable/ArrayOps$ofRef."<init>":([Ljava/lang/Ob
ject;)V
        31: invokevirtual #78                 // Method scala/collection/mutable/ArrayOps$ofRef.tail:()Ljava/lang/Object
;
        34: checkcast     #67                 // class "[Ljava/lang/Object;"
        37: invokevirtual #71                 // Method scala/Predef$.refArrayOps:([Ljava/lang/Object;)[Ljava/lang/Objec
t;
        40: invokespecial #74                 // Method scala/collection/mutable/ArrayOps$ofRef."<init>":([Ljava/lang/Ob
ject;)V
        43: invokevirtual #82                 // Method scala/collection/mutable/ArrayOps$ofRef.nonEmpty:()Z
        46: ifeq          79
        49: new           #84                 // class scala/Some
        52: dup
        53: new           #7                  // class scala/collection/mutable/ArrayOps$ofRef
        56: dup
        57: getstatic     #65                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
        60: aload_2
        61: checkcast     #67                 // class "[Ljava/lang/Object;"
        64: invokevirtual #71                 // Method scala/Predef$.refArrayOps:([Ljava/lang/Object;)[Ljava/lang/Objec
t;
        67: invokespecial #74                 // Method scala/collection/mutable/ArrayOps$ofRef."<init>":([Ljava/lang/Ob
ject;)V
        70: invokevirtual #87                 // Method scala/collection/mutable/ArrayOps$ofRef.head:()Ljava/lang/Object
;
        73: invokespecial #90                 // Method scala/Some."<init>":(Ljava/lang/Object;)V
        76: goto          82
        79: getstatic     #95                 // Field scala/None$.MODULE$:Lscala/None$;
        82: areturn

模式匹配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class person(id:Integer, name:String) {
  def f(plan: AnyVal): Unit = {
    val res = plan match {
      case a @ AppendDelta(r, d) if !a.isByName => "1111"
      case o @ OverwriteDelta(r, d) if !o.isByName => "222"
      case a @ AppendDelta(r, d) => "333"
      case a:Aggregate => "444"
      case _=> "555"
    }
  }

反编译后如下,对应的 JVM 层,就是一堆的 instanceof

 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
  public void f(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=11, args_size=2
         0: iconst_0
         1: istore        4
         3: aconst_null
         4: astore        5
         6: aload_1
         7: astore        6
         9: aload         6
        11: instanceof    #13                 // class org/apache/spark/sql/catalyst/plans/logical/AppendData
        14: ifeq          62
        17: iconst_1
        18: istore        4
        20: aload         6
        22: checkcast     #13                 // class org/apache/spark/sql/catalyst/plans/logical/AppendData
        25: astore        5
        27: getstatic     #19                 // Field org/apache/spark/sql/delta/AppendDelta$.MODULE$:Lorg/apache/spark
/sql/delta/AppendDelta$;
        30: aload         5
        32: invokevirtual #23                 // Method org/apache/spark/sql/delta/AppendDelta$.unapply:(Lorg/apache/spa
rk/sql/catalyst/plans/logical/AppendData;)Lscala/Option;
        35: astore        7
        37: aload         7
        39: invokevirtual #29                 // Method scala/Option.isEmpty:()Z
        42: ifne          59
        45: aload         5
        47: invokevirtual #32                 // Method org/apache/spark/sql/catalyst/plans/logical/AppendData.isByName:
()Z
        50: ifne          59
        53: ldc           #34                 // String 1111
        55: astore_2
        56: goto          176
        59: goto          65
        62: goto          65
        65: aload         6
        67: instanceof    #36                 // class org/apache/spark/sql/catalyst/plans/logical/OverwriteByExpression

高阶函数

多参数列表(柯里化)

类型上界和下界

隐式转换

泛类型

单例对象

一些语法特性

函数柯里化表示

内部函数
比如说,有这么一个类:

1
2
3
4
5
6
7
8
class gg() {
  def x(i: Int): Unit = {
    def inner_haha(a:String, b:String, c:String) = {
      println("i -> " + i)
    }
    inner_haha("","","")
  }
}

对于 JVM 字节码来说,是没法表示内嵌函数的,那么通过 dump 字节码看下结果:

 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
  public void x(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=2
         0: ldc           #13                 // String
         2: ldc           #13                 // String
         4: ldc           #13                 // String
         6: iload_1
         7: invokestatic  #17                 // Method inner_haha$1:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Str
ing;I)V
        10: return
      
	  
  private static final void inner_haha$1(java.lang.String, java.lang.String, java.lang.String, int);
    descriptor: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    Code:
      stack=4, locals=4, args_size=4
         0: getstatic     #30                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         3: new           #32                 // class java/lang/StringBuilder
         6: dup
         7: ldc           #33                 // int 5
         9: invokespecial #36                 // Method java/lang/StringBuilder."<init>":(I)V
        12: ldc           #38                 // String i ->
        14: invokevirtual #42                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/St
ringBuilder;
        17: iload_3
        18: invokevirtual #45                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        21: invokevirtual #49                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #53                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
        27: return	 

从字节码看, 是将 内部函数,变成了一个普通的 私有静态函数,同时给这个函数的参数列表最后,增加了一个int
当调用 inner_haha("","","") 时候,在 JVM 运行时看来,是调用 inner_haha("","","", i)