IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    scala雾中风景(28): private?public?

    hongjiang发表于 2017-05-23 14:49:43
    love 0

    记录前些天遇到的一个问题,scala里protected或private修饰的方法可能在编译为class时变成了public,这已经不是第一次遇到,最早遇到是在写一个java子类时要覆盖一些父类方法,父类是scala写的一个trait,里面的方法修饰为protected,当时IDE提示我override的方法必须声明为public感到奇怪反编译了一下父trait果然被声明为了public。

    而这次遇到的稍有不同,跟继承没有关系,用下面的demo举例:

     ➜  cat A.scala
    class A {
      private[this] def foo() = {
        List(1,2,3).map(i => bar(i))
      }
    
      private[this] def bar(i:Int):String = {
        "str:" + i
      }
    }
    

    当我们编译上面类之后,里面的foo和bar方法的修饰符最终在class里会有所不同,反编译后可看到bar修饰符变成了public:

     ➜  cfr-decompiler A
     ...
     public class A {
        private List<String> foo() {
            return (List)List..MODULE$.apply((Seq)Predef..MODULE$.wrapIntArray(new int[]{1, 2, 3})).map((Function1)new scala.Serializable(this){
                public static final long serialVersionUID = 0;
                private final /* synthetic */ A $outer;
    
                public final String apply(int i) {
                    return this.$outer.A$$bar(i);
                }
            }, List..MODULE$.canBuildFrom());
        }
    
        public String A$$bar(int i) {
            return new StringBuilder().append((Object)"str:").append((Object)BoxesRunTime.boxToInteger((int)i)).toString();
        }
    }

    终归scala在jvm上要做一些妥协,按上面的实现,foo里面以闭包的方式使用bar的时候,如果保持scala private[this]的控制粒度,底层的匿名类其实已经无法访问bar了。所以scala在编译器的explicitouter环节做了一些向现实妥协的事情

     ➜  scalac -Xshow-phases
        phase name  id  description
        ----------  --  -----------
            parser   1  parse source into ASTs, perform simple desugaring
             namer   2  resolve names, attach symbols to named trees
    packageobjects   3  load package objects
             typer   4  the meat and potatoes: type the trees
            patmat   5  translate match expressions
    superaccessors   6  add super accessors in traits and nested classes
        extmethods   7  add extension methods for inline classes
           pickler   8  serialize symbol tables
         refchecks   9  reference/override checking, translate nested objects
           uncurry  10  uncurry, translate function values to anonymous classes
         tailcalls  11  replace tail calls by jumps
        specialize  12  @specialized-driven class and method specialization
     explicitouter  13  this refs to outer pointers
           erasure  14  erase types, add interfaces for traits
       posterasure  15  clean up erased inline classes
          lazyvals  16  allocate bitmaps, translate lazy vals into lazified defs
        lambdalift  17  move nested functions to top level
      constructors  18  move field definitions into constructors
           flatten  19  eliminate inner classes
             mixin  20  mixin composition
           cleanup  21  platform-specific cleanups, generate reflective calls
        delambdafy  22  remove lambdas
             icode  23  generate portable intermediate code
               jvm  24  generate JVM bytecode
          terminal  25  the last phase during a compilation run

    在这个阶段,当编译器发现一些private的方法会被内部类访问的话,就删除这些private修饰符:

     ➜  scalac -Xprint:explicitouter A.scala
    [[syntax trees at end of             explicitouter]] // A.scala
    package <empty> {
      class A extends Object {
        def <init>(): A = {
          A.super.<init>();
          ()
        };
        private[this] def foo(): List[String] = immutable.this.List.apply[Int](scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3})).map[String, List[String]]({
          @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractFunction1[Int,String] with Serializable {
            def <init>($outer: A.this.type): <$anon: Int => String> = {
              $anonfun.super.<init>();
              ()
            };
            final def apply(i: Int): String = $anonfun.this.$outer.bar(i);
            <synthetic> <paramaccessor> <artifact> private[this] val $outer: A.this.type = _;
            <synthetic> <stable> <artifact> def $outer(): A.this.type = $anonfun.this.$outer
          };
          (new <$anon: Int => String>(A.this): Int => String)
        }, immutable.this.List.canBuildFrom[String]());
        
        final def bar(i: Int): String = "str:".+(i)
      }
    }

    上面bar的private[this]在这个阶段被删除,而scala不同于java,缺省就是public,最终在class里变成了public。



沪ICP备19023445号-2号
友情链接