Decompiling Traits with Scala 2.11 and 2.12

综合编程 Asyncified (源链)

Introduction

When working with Scala, one quickly gets familiar with the notation of Traits
. Traits enable powerful language features such as Subtype Polymorphism
and Mixins
.

Scala 2.12 (currently at preview stage M4) takes advantage of Java 8 Default Methods
to generate optimized JVM byte code for traits. We’ll take a short detour of how Scala 2.11 generates byte code for traits and then look at Scala 2.12 , both compiled with Java 8.

Compiling traits with Scala 2.11

Traits in Scala allow us to provide a default implementation to interface methods:

trait X {
  def helloWorld: String = "hello world"
}

Using Scala on the JVM, the compiler needs to work around the fact that Java <= v7 doesn’t allow default implementations on interfaces, which is the relative cousin of traits. Lets see what the Scala compiler does to work around that. We’ll compile trait X
and then look at the generated byte code:

[root@localhost yuvie]# scalac X.scala
[root@localhost yuvie]# cd yuvie/
[root@localhost yuvie]# ll
total 12
-rw-r--r--. 1 root   root   448 Jun 24 17:58 X.class
-rw-r--r--. 1 root   root   416 Jun 24 17:58 X$class.class

Scala generated two class files for our X
trait. One called X.class
, and one called X$class.class
. Let’s look at what each of these contain:

[root@localhost yuvie]# javap -c -p X
Warning: Binary file X contains yuvie.X
Compiled from "X.scala"
public interface yuvie.X {
  public abstract java.lang.String helloWorld();
}

[root@localhost yuvie]# javap -c -p X$class.class 
Compiled from "X.scala"
public abstract class yuvie.X$class {
  public static java.lang.String helloWorld(yuvie.X);
    Code:
       0: ldc           #9                  // String hello world
       2: areturn

  public static void $init$(yuvie.X);
    Code:
       0: return
}

Looking at the code we can see that Scalas compiler generates:

  1. An interface called X
    , matching the trait declaration. This interface has a single method called helloWorld
    without the implementation
    .
  2. An abstract class called X$class
    with a static
    helloWorld
    method, which provides the implementation.

Lets see what happens when we extend X
with some class M
:

class M extends X

Bytecode:

[root@localhost yuvie]# javap -c -p M.class 
Compiled from "M.scala"
public class yuvie.M implements yuvie.X {
  public java.lang.String helloWorld();
    Code:
       0: aload_0
       1: invokestatic  #17                 // Method yuvie/X$class.helloWorld:(Lyuvie/X;)Ljava/lang/String;
       4: areturn

  public yuvie.M();
    Code:
       0: aload_0
       1: invokespecial #23                 // Method java/lang/Object."":()V
       4: aload_0
       5: invokestatic  #27                 // Method yuvie/X$class.$init$:(Lyuvie/X;)V
       8: return
}

When we extend / mixin X
and want to invoke helloWorld
, a call to the abstract class X$class
is made which provides the implementation for helloWorld
. This duo of interface and abstract class allows Scala to generate valid byte code.

Leveraging Default Methods with Scala 2.12

We just saw how Scala 2.11 deals with compiling traits, let’s see how Scala 2.12 leverages default methods. Taking the same code and compiling it again only now with Scala 2.12-M4 yields the following:

[root@localhost test-project]# cd target/scala-2.12.0-M4/classes/yuvie/
[root@localhost yuvie]# ll
total 16
-rw-rw-r--. 1 yuvali yuvali  680 Jun 24 18:17 X.class

[root@localhost yuvie]# javap -c -p X.class
Compiled from "X.scala"
public interface yuvie.X {
  public java.lang.String helloWorld();
    Code:
       0: ldc           #12                 // String hello world
       2: areturn

  public void $init$();
    Code:
       0: return
}

The compiler generates a single
interface
called X
which has the default implementation of the trait. Let’s see what happens now when we extend M
with X
:

[root@localhost yuvie]# javap -c -p M.class 
Compiled from "M.scala"
public class yuvie.M implements yuvie.X {
  public java.lang.String helloWorld();
    Code:
       0: aload_0
       1: invokespecial #14                 // Method yuvie/X.helloWorld:()Ljava/lang/String;
       4: areturn

  public yuvie.M();
    Code:
       0: aload_0
       1: invokespecial #20                 // Method java/lang/Object."":()V
       4: aload_0
       5: invokespecial #23                 // Method yuvie/X.$init$:()V
       8: return
}

The compiler now generates an invokespecial
instruction for the instance method
created in X
, instead of invokestatic
previously for the static method
created inside X$class.class
.

Conclusion

Thanks to Java 8 and Default Method, Scala 2.12 is now able to emit less byte code for traits. My example was very simplified, and in real case scenarios this should save decent amount of bytecode generation for the Scala compiler and enables Scala to be more aligned with Java without needing to jump the hoops to make traits work.

您可能感兴趣的

Scala(一)——scala+Idea环境配置 Java虚拟机的确是很强大,有很多计算机语言可以运行在虚拟机上,完善了虚拟机上多语言编程。 近年来,大数据云计算,大数据的火爆也让一些小众语言火了起来,如Python,Scala等。这些语言编写简单,自带计算函数。今天我们就开始Scala语言的学习。 Scala2.12.6 + Ide...
Scala 与设计模式(一):Singleton 单例模式... 二十年前,软件设计领域的四位大师( GoF ,”四人帮”,又称 Gang of Four,即Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides)通过论著《设计模式:可复用面向对象软件的基础》阐述了设计模式领域的开创性成果。设计...
Recognising Handwritten Digits with Scala doddle-model: immutable machine learning in Scala. In the past few months I’ve started writing Scala code extensively ...
Integrate Scala tools with your workflow with Buil... © Shutterstock / muk woothimanop Instead of asking developers to learn a new IDE or build tool, is it possible to ship tools that integ...
Scala 技术周刊 | 第9期 这里有最新的 Scala 社区动态、技术博文。 微信搜索 「scalacool」关注我们,及时获取最新资讯。 深度阅读 Akka monitoring with Kamon part 2 用...
责编内容来自:Asyncified (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » Decompiling Traits with Scala 2.11 and 2.12



专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录