zoukankan      html  css  js  c++  java
  • Scala中的语言特性是如何实现的(1)

    Scala可以编译为Java bytecode和CIL,从而在JVM和CLI之上运行。Scala有很多在Java和C#的世界中显得陌生的语言特性,本文将分析这些语言特性是如何实现的。

    object

    Scala中可以像这样创建object:

    1
    2
    3
    4
    5
    
    object HowIsObjectImplementedInScala {
      def printSomething() {
        println("printSomething")
      }
    }
    

    然后在代码的其他地方调用printSomething,一个object究竟是什么东西呢? 我们将这段Scala编译为Java bytecode,然后反编译为Java,会发现编译器为HowIsObjectImplementedInScala这个object生成了两个类:

    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
    
    public final class HowIsObjectImplementedInScala
    {
      public static void printSomething()
      {
        HowIsObjectImplementedInScala..MODULE$.printSomething();
      }
    }
    public final class HowIsObjectImplementedInScala$
    {
      public static final  MODULE$;
      static
      {
        new ();
      }
      public void printSomething()
      {
        Predef..MODULE$.println("printSomething");
      }
      private HowIsObjectImplementedInScala$()
      {
        MODULE$ = this;
      }
    }
    

    第一个类只包含一个静态方法,其实现依赖于第二个叫做HowIsObjectImplementedInScala$的类。

    HowIsObjectImplementedInScala$是一个单例,其静态块实例化自己并把this赋值给MODULE$这个public static的成员,从而可以被外界访问。

    同样,我们可以把这段代码编译为CIL,然后反编译为C#:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    public sealed class HowIsObjectImplementedInScala
    {
      public static void printSomething()
      {
        HowIsObjectImplementedInScala$.MODULE$.printSomething();
      }
    }
    public sealed class HowIsObjectImplementedInScala$ : ScalaObject
    {
      public static HowIsObjectImplementedInScala$ MODULE$;
      public override void printSomething()
      {
        Predef$.MODULE$.println("printSomething");
      }
      private HowIsObjectImplementedInScala$()
      {
        HowIsObjectImplementedInScala$.MODULE$ = this;
      }
      static HowIsObjectImplementedInScala$()
      {
        new HowIsObjectImplementedInScala$();
      }
    }
    

    和Java代码大同小异,除了静态构造和某几个关键字外,基本一样。一个object就是一个Scala编译器帮我们实现的singleton。

    var和val

    var:可变。val:不可变。关于这两个关键字何时该使用哪一个,这里不做讨论,我们只是观察这二者在编译后是如何被表示的。

    这段Scala代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    class HowAreVarAndValImplementedInScala {
      var v1 = 123
      val v2 = 456
      def method1() = {
        var v3 = 123
        val v4 = 456
        println(v3 + v4)
      }
    }
    

    定义了两个字段一个var,一个val,方法中定义了两个局部变量,一个var,一个val。

    编译为Java bytecode并反编译之后:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    public class HowAreVarAndValImplementedInScala
    {
      private int v1 = 123;
      private final int v2 = 456;
      public int v1()
      {
        return this.v1;
      }
      public void v1_$eq(int x$1) { this.v1 = x$1; }
      public int v2() { return this.v2; }
      public void method1() {
        int v3 = 123;
        int v4 = 456;
        Predef..MODULE$.println(BoxesRunTime.boxToInteger(v3 + v4));
      }
    }
    

    声明为字段的v1和v2,一个是普通字段,另一个则被标记为final。编译器为v1生成了getter和setter,为v2则只有getter,因为v2作为immutable的字段是不可以被重新赋值的。

    有趣的是方法中的局部变量都是普通的变量,没有被final修饰。

    再来看这段Scala编译为CIL再反编译为C#之后的样子:

    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
    
    public class HowAreVarAndValImplementedInScala : ScalaObject
    {
      private int v1;
      private int v2;
      public override int v1()
      {
        return this.v1;
      }
      public override void v1_$eq(int x$1)
      {
        this.v1 = x$1;
      }
      public override int v2()
      {
        return this.v2;
      }
      public override void method1()
      {
        int v3 = 123;
        int v4 = 456;
        Predef$.MODULE$.println(v3 + v4);
      }
      public HowAreVarAndValImplementedInScala()
      {
        this.v1 = 123;
        this.v2 = 456;
      }
    }
    

    有一个明显的问题,v2没有标为readonly(C#世界中用于声明变量不可以重新赋值的关键字),这是compiler的bug吗?

    除此之外,和Java代码一致。但是有趣的是代码中的所有public方法(包括上一段演示object的代码)都被标为了override,原因不明。

    小结

    本来以为研究这么简单的两个语言特性不会有啥收获,仅仅是反编译一下,看看compiler都做了啥,满足下好奇心罢了。

    结果还是有意外收获,我在反编译后的代码中发现了三个有趣的问题:

    • 在Scala中被声明为val的v4为什么在反编译的Java中不是final的呢?
    • 在Scala中被声明为val的v2为什么在反编译的C#中不是readonly的呢?
    • 为什么反编译出来的C#代码中的实例级公开方法都是标有override的呢?

    为什么呢?为什么呢?为什么呢?答案下期揭晓。

  • 相关阅读:
    手机截屏雷阵雨
    2010年5月20日 用户的数据永远不会靠谱
    2010年5月17日 OCD
    2010年8月9日 流水账
    2010年5月19日 借鉴
    立表为据
    2010年6月2日 回来
    不重视小C打屁屁
    2010年8月16日 知行合一
    2010年5月18日 小细节大隐患
  • 原文地址:https://www.cnblogs.com/cuipengfei/p/3070169.html
Copyright © 2011-2022 走看看