【问题标题】:Strange behavour of bytecode generated for Scala code为 Scala 代码生成的字节码的奇怪行为
【发布时间】:2018-04-07 12:13:16
【问题描述】:

我正在使用 Scala 特性。

trait Animal{
  def speak = println("speaking..")
  def comeToMaster: Unit
}

class Cat extends Animal{
   override def speak: Unit = println("meow....")
   def comeToMaster = println("catch me if you can..")
}

object Sample extends App{
 val kity = new Cat
  kity.speak
  kity.comeToMaster
}

但是当我看到 Scala 编译器生成的字节码时,

javap -c Animal.class 
Compiled from "Sample.scala"
public abstract class Animal$class {
  public static void speak(Animal);
    Code:
       0: getstatic     #13                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #15                 // String speaking..
       5: invokevirtual #19                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

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

注意字节码中只有 speak 方法。 comeToMaster 方法在哪里?删除所有类文件后我再次尝试。不过,它给了我同样的结果。所以,我没有得到 Scala 编译器生成的 Java 字节码的这种奇怪行为。

但是当我看到 Cat 类的字节码时,两种方法都有。

javap -c Cat.class 
Compiled from "Sample.scala"
public class Cat implements Animal {
  public void speak();
    Code:
       0: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #20                 // String meow....
       5: invokevirtual #24                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public void comeToMaster();
    Code:
       0: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #29                 // String catch me if you can..
       5: invokevirtual #24                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public Cat();
    Code:
       0: aload_0
       1: invokespecial #32                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: invokestatic  #38                 // Method Animal$class.$init$:(LAnimal;)V
       8: return
}

【问题讨论】:

  • Animal.comeToMaster 没有定义。你想知道为什么没有生成抽象方法吗?也许这与子类中缺少 override 有关?
  • 是的,为什么它没有在那里定义。在这种情况下不需要覆盖。它都不影响字节码的生成。

标签: scala interface abstract-class traits bytecode


【解决方案1】:

这应该取决于版本。我的输出是

$ javap -c Cat.class

Compiled from "Animal.scala"
public class Cat implements Animal {
  public void speak();
    Code:
       0: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #20                 // String meow....
       5: invokevirtual #24                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public void comeToMaster();
    Code:
       0: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #29                 // String catch me if you can..
       5: invokevirtual #24                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public Cat();
    Code:
       0: aload_0
       1: invokespecial #32                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: invokestatic  #36                 // InterfaceMethod Animal.$init$:(LAnimal;)V
       8: return
}

$ javap -c Animal.class

Compiled from "Animal.scala"
public interface Animal {
  public static void speak$(Animal);
    Code:
       0: aload_0
       1: invokespecial #15                 // InterfaceMethod speak:()V
       4: return

  public void speak();
    Code:
       0: getstatic     #22                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #24                 // String speaking..
       5: invokevirtual #28                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public abstract void comeToMaster();

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

Scala 2.12.4 (jdk 1.8.0_162)

尝试在您的目录中查看类。 可能除了抽象类Animal$class,你还有接口Animal


Animal.scala 放在一个目录中,将Animal$class.scala 放在另一个目录中,然后到处调用javap。使用 Scala 2.11.12 输出是

$ javap -c Animal.class

Compiled from "Animal.scala"
public interface Animal {
  public abstract void speak();

  public abstract void comeToMaster();
}

$ javap -c Animal$class.class

Compiled from "Animal.scala"
public abstract class Animal$class {
  public static void speak(Animal);
    Code:
       0: getstatic     #13                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #15                 // String speaking..
       5: invokevirtual #19                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

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

【讨论】:

  • 是的,我知道,Animal.class 会有接口,Animal$class.class 会有抽象类。我试图打印两个,但结果是一样的。
  • 好吧,那么这是 javap 的奇怪行为,而不是字节码的奇怪行为。尝试使用不同的反编译器(javap 可以更面向 Java 而更少面向 Scala)。例如 IntelliJ IDEA 的插件:Bytecode Viewer、ASM Bytecode Outline。
  • 是的,这很奇怪。我会试试的。
  • 这很有趣。我用 Scala 2.11.12 检查了与 javap Bytecode Viewer 和 ASM Bytecode Outline 不同的是显示Animal(抽象方法comeToMaster 存在)而不是Animal$class。显然,当有多个类时,反编译器会严重猜测要显示哪些类。
  • 有解决方法。将Animal.scala 放在一个目录中,将Animal$class.scala 放在另一个目录中,然后到处调用javap。我正在更新我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
  • 2021-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多