Introduction to Scala from a Java perspective, decompiled and understood (basic)

This article is an in-house technology community of Fujitsu Systems Web Technology, "Innovation Promotion Community" This is the article on the 14th day of the Inobeko Summer Vacation Advent Calendar 2020, which is planned by "Inobeko" for short. The content of this article is my own opinion and does not represent the organization to which I belong. Promise so far: wink:

Introduction

In this article, a Java programmer who started Scala decompiles the generated class file by compiling Scala. I started thinking that I wanted to understand Scala from a Java perspective.

I would like to proceed with the basic flow as follows.

That's it.

Let's get a little touch on the world of Scala. This time, I would like to touch on the world of Scala using the "basic" edition from TOUR OF SCALA. (There are some development issues)

class

Classes in scala look almost the same as Java, but with differences in constructor parameters. Also, let's check how to implement overload based on the sample.

Sample code

class Greeter(prefix: String, suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}

After decompilation

package com.github.fishibashi.scaladecompile;

import scala.Predef$;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes = "\006\005A2A!\002\004\001\037!Aa\003\001B\001B\003%q\003\003\005#\001\t\005\t\025!\003\030\021\025\031\003\001\"\001%\021\025I\003\001\"\001+\005\0359%/Z3uKJT!a\002\005\002\035M\034\027\r\\1eK\016|W\016]5mK*\021\021BC\001\013M&\034\b.\0332bg\"L'BA\006\r\003\0319\027\016\0365vE*\tQ\"A\002d_6\034\001a\005\002\001!A\021\021\003F\007\002%)\t1#A\003tG\006d\027-\003\002\026%\t1\021I\\=SK\032\fa\001\035:fM&D\bC\001\r \035\tIR\004\005\002\033%5\t1D\003\002\035\035\0051AH]8pizJ!A\b\n\002\rA\023X\rZ3g\023\t\001\023E\001\004TiJLgn\032\006\003=I\taa];gM&D\030A\002\037j]&$h\bF\002&O!\002\"A\n\001\016\003\031AQAF\002A\002]AQAI\002A\002]\tQa\032:fKR$\"a\013\030\021\005Ea\023BA\027\023\005\021)f.\033;\t\013=\"\001\031A\f\002\t9\fW.\032")
public class Greeter {
  private final String prefix;
  
  private final String suffix;
  
  public Greeter(String prefix, String suffix) {}
  
  public void greet(String name) {
    Predef$.MODULE$.println((new StringBuilder(0)).append(this.prefix).append(name).append(this.suffix).toString());
  }
}

ScalaSignature

It seems that it stores the meta information when it was compiled. There was an article on qiita that confirmed how to implement sealed. https://qiita.com/ocadaruma/items/93818bd1a5318e71f0d8

I don't know if it's something you want to know, but it's for that. .. ..

Constructor arguments

The arguments prefix: String, suffix: String have been defined as private final fields.

As a test, if you write the constructor parameter as a var parameter, it will be as follows.

class Greeter(var prefix: String, var suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}
public class Greeter {
  private String prefix;
  
  private String suffix;
  
  public String prefix() {
    return this.prefix;
  }
  
  public void prefix_$eq(String x$1) {
    this.prefix = x$1;
  }
  
  public String suffix() {
    return this.suffix;
  }
  
  public void suffix_$eq(String x$1) {
    this.suffix = x$1;
  }
  
  public Greeter(String prefix, String suffix) {}
  
  public void greet(String name) {
    Predef$.MODULE$.println((new StringBuilder(0)).append(prefix()).append(name).append(suffix()).toString());
  }
}

You can see that the final is gone and the getter methods (prefix (), suffix ()) and the setter method ($ eq) are generated. When calling from scala code, it seems that you can get and set with prefix normally, but when calling from Java code, it seems to be field name _ $ eq. It's complicated ...

greet method

In the greet method, strings were concatenated with the + method (in scala, + is also treated as a method!). However, when I look at the result of decompilation, I am using StringBuilder to concatenate the strings. What! Smart! If you want to combine strings in Java code and process them, there is no doubt that ** "Use StringBuilder: angry:" will be written in the review table, but in the case of Scala, that is the case. Don't worry.

The result of rewriting in Java and decompiling is as follows.

JavaGreeeter.java


//Precompiled code
//public class JavaGreeter {
//    private final String prefix;
//
//    private final String suffix;
//
//    public JavaGreeter(String prefix, String suffix) {
//        this.prefix = prefix;
//        this.suffix = suffix;
//    }
//
//    public String greet(String name) {
//        return prefix + name + suffix;
//    }
//}

import scala.Predef$;

public class JavaGreeter {
  private final String prefix;
  
  private final String suffix;
  
  public JavaGreeter(String prefix, String suffix) {
    this.prefix = prefix;
    this.suffix = suffix;
  }
  
  public void greet(String name) {
    Predef$.MODULE$.println(this.prefix + this.prefix + name);
  }
}

It seems that it is designed to simply combine strings. Perhaps Scala treats all operators as functions as well. I imagine that the + method of String itself is probably moving to use StringBuilder (maybe scalac is supposed to interpret it with StringBuilder ...).

Even if you use literals, it seems that StringBuilder will be used in the same way.

  def greet(name: String): Unit =
    println(s"${prefix} ${name} ${suffix}")
  /**
    public void greet(String name) {
      Predef$.MODULE$.println((new StringBuilder(2)).append(prefix()).append(" ").append(name).append(" ").append(suffix()).toString());
    }
   */

object

Next is ʻobject. It's not Java's java.lang.Object. Does Scala's ʻobject correspond to the Singleton pattern in Java? The syntax is as follows.

Sample code

object IdFactory {
  private var counter = 0
  def create(): Int = {
    counter += 1
    counter
  }
}

After decompilation

IdFactory.class


@ScalaSignature(bytes = "\006\0051:Qa\002\005\t\002E1Qa\005\005\t\002QAQaG\001\005\002qAq!H\001A\002\023%a\004C\004#\003\001\007I\021B\022\t\r%\n\001\025)\003 \021\025Q\023\001\"\001,\003%IEMR1di>\024\030P\003\002\n\025\005q1oY1mC\022,7m\\7qS2,'BA\006\r\003)1\027n\0355jE\006\034\b.\033\006\003\0339\taaZ5uQV\024'\"A\b\002\007\r|Wn\001\001\021\005I\tQ\"\001\005\003\023%#g)Y2u_JL8CA\001\026!\t1\022$D\001\030\025\005A\022!B:dC2\f\027B\001\016\030\005\031\te.\037*fM\0061A(\0338jiz\"\022!E\001\bG>,h\016^3s+\005y\002C\001\f!\023\t\tsCA\002J]R\f1bY8v]R,'o\030\023fcR\021Ae\n\t\003-\025J!AJ\f\003\tUs\027\016\036\005\bQ\021\t\t\0211\001 \003\rAH%M\001\tG>,h\016^3sA\00511M]3bi\026$\022a\b")
public final class IdFactory {
  public static int create() {
    return IdFactory$.MODULE$.create();
  }
}

IdFactory$.class


public final class IdFactory$ {
  public static final IdFactory$ MODULE$ = new IdFactory$();
  
  private static int counter = 0;
  
  private int counter() {
    return counter;
  }
  
  private void counter_$eq(int x$1) {
    counter = x$1;
  }
  
  public int create() {
    counter_$eq(counter() + 1);
    return counter();
  }
}

What! The anonymous class is complete.

Probably, it consists of a Singleton object ʻIdFactory $ and a ʻIdFactory class that calls it. Is there a reason to be separated? This is the end of this time because I thought there was something to do in my studies. The official documentation also says later that it will be dealt with in detail, so I'm looking forward to it.

trait

What is a trait? It's like an interface, but it seems that it can have fields, can be implemented by default, or can inherit (mixin) multiple traits.

Here, let's create a trait and create an inherited class to see what kind of class is created.

Sample code

trait Greeter {
  def greet(name: String): Unit =
    println("Hello, " + name + "!")
}

class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
  override def greet(name: String): Unit = {
    println(prefix + name + postfix)
  }
}

After decompilation

This time, a total of 3 class files have been created. In the case of Java, it is not possible to create multiple public classes in one java source code, so it was divided into multiple.

Greeter.java


@ScalaSignature(bytes = "\006\005!2qa\001\003\021\002\007\005Q\002C\003\025\001\021\005Q\003C\003\032\001\021\005!DA\004He\026,G/\032:\013\005\0251\021AD:dC2\fG-Z2p[BLG.\032\006\003\017!\t!BZ5tQ&\024\027m\0355j\025\tI!\"\001\004hSRDWO\031\006\002\027\005\0311m\\7\004\001M\021\001A\004\t\003\037Ii\021\001\005\006\002#\005)1oY1mC&\0211\003\005\002\007\003:L(+\0324\002\r\021Jg.\033;%)\0051\002CA\b\030\023\tA\002C\001\003V]&$\030!B4sK\026$HC\001\f\034\021\025a\"\0011\001\036\003\021q\027-\\3\021\005y)cBA\020$!\t\001\003#D\001\"\025\t\021C\"\001\004=e>|GOP\005\003IA\ta\001\025:fI\0264\027B\001\024(\005\031\031FO]5oO*\021A\005\005")
public interface Greeter {
  static void $init$(Greeter $this) {}
  
  default void greet(String name) {
    Predef$.MODULE$.println((new StringBuilder(8)).append("Hello, ").append(name).append("!").toString());
  }
}

trait is now interface

DefaultGreeter.java


@ScalaSignature(bytes = "\006\005i1AAA\002\001\031!)q\003\001C\0011\tqA)\0324bk2$xI]3fi\026\024(B\001\003\006\0039\0318-\0317bI\026\034w.\0349jY\026T!AB\004\002\025\031L7\017[5cCND\027N\003\002\t\023\0051q-\033;ik\nT\021AC\001\004G>l7\001A\n\004\0015\031\002C\001\b\022\033\005y!\"\001\t\002\013M\034\027\r\\1\n\005Iy!AB!osJ+g\r\005\002\025+5\t1!\003\002\027\007\t9qI]3fi\026\024\030A\002\037j]&$h\bF\001\032!\t!\002\001")
public class DefaultGreeter implements Greeter {
  public void greet(String name) {
    Greeter.greet$(this, name);
  }
  
  public DefaultGreeter() {
    Greeter.$init$(this);
  }
}

It is in the form of calling the default implementation of the interface.

python


@ScalaSignature(bytes = "\006\005M2A!\002\004\001\037!A!\004\001B\001B\003%1\004\003\005'\001\t\005\t\025!\003\034\021\0259\003\001\"\001)\021\025a\003\001\"\021.\005M\031Uo\035;p[&T\030M\0317f\017J,W\r^3s\025\t9\001\"\001\btG\006d\027\rZ3d_6\004\030\016\\3\013\005%Q\021A\0034jg\"L'-Y:iS*\0211\002D\001\007O&$\b.\0362\013\0035\t1aY8n\007\001\0312\001\001\t\027!\t\tB#D\001\023\025\005\031\022!B:dC2\f\027BA\013\023\005\031\te.\037*fMB\021q\003G\007\002\r%\021\021D\002\002\b\017J,W\r^3s\003\031\001(/\0324jqB\021Ad\t\b\003;\005\002\"A\b\n\016\003}Q!\001\t\b\002\rq\022xn\034;?\023\t\021##\001\004Qe\026$WMZ\005\003I\025\022aa\025;sS:<'B\001\022\023\003\035\001xn\035;gSb\fa\001P5oSRtDcA\025+WA\021q\003\001\005\0065\r\001\ra\007\005\006M\r\001\raG\001\006OJ,W\r\036\013\003]E\002\"!E\030\n\005A\022\"\001B+oSRDQA\r\003A\002m\tAA\\1nK\002")
public class CustomizableGreeter implements Greeter {
  private final String prefix;
  
  private final String postfix;
  
  public CustomizableGreeter(String prefix, String postfix) {
    Greeter.$init$(this);
  }
  
  public void greet(String name) {
    Predef$.MODULE$.println((new StringBuilder(0)).append(this.prefix).append(name).append(this.postfix).toString());
  }
}

It seems that it just overrides the implemented implementation. Well, it's expected.

mixin

Scala allows you to synthesize multiple classes using mixins. Java cannot inherit multiple classes or Interfaces, it can inherit a single parent class.

Sample code using mixin

Mixin.scala


abstract class BaseMixinSample(val prefix: String, val suffix: String) {
  def greet(name: String): Unit = println(prefix + name + suffix)
}

trait TraitA {
  val a: String
  def A(): Unit = println("A")
}

trait TraitB {
  val b: String
  def B(): Unit = println("B")
}

class DefaultMixinSample() extends BaseMixinSample("Hello", "!") with TraitA with TraitB {
  override def greet(name: String): Unit = super.greet(name)

  override val a: String = "Value is A"
  override val b: String = "Value is B"
}

After decompilation


import scala.Predef$;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes = "\006\005U2Qa\002\005\002\002EA\001\002\007\001\003\006\004%\t!\007\005\tK\001\021\t\021)A\0055!Aa\005\001BC\002\023\005\021\004\003\005(\001\t\005\t\025!\003\033\021\025A\003\001\"\001*\021\025q\003\001\"\0010\005=\021\025m]3NSbLgnU1na2,'BA\005\013\0039\0318-\0317bI\026\034w.\0349jY\026T!a\003\007\002\025\031L7\017[5cCND\027N\003\002\016\035\0051q-\033;ik\nT\021aD\001\004G>l7\001A\n\003\001I\001\"a\005\f\016\003QQ\021!F\001\006g\016\fG.Y\005\003/Q\021a!\0218z%\0264\027A\0029sK\032L\0070F\001\033!\tY\"E\004\002\035AA\021Q\004F\007\002=)\021q\004E\001\007yI|w\016\036 \n\005\005\"\022A\002)sK\022,g-\003\002$I\t11\013\036:j]\036T!!\t\013\002\017A\024XMZ5yA\00511/\0364gSb\fqa];gM&D\b%\001\004=S:LGO\020\013\004U1j\003CA\026\001\033\005A\001\"\002\r\006\001\004Q\002\"\002\024\006\001\004Q\022!B4sK\026$HC\001\0314!\t\031\022'\003\0023)\t!QK\\5u\021\025!d\0011\001\033\003\021q\027-\\3")
public abstract class BaseMixinSample {
  private final String prefix;
  
  private final String suffix;
  
  public String prefix() {
    return this.prefix;
  }
  
  public String suffix() {
    return this.suffix;
  }
  
  public BaseMixinSample(String prefix, String suffix) {}
  
  public void greet(String name) {
    Predef$.MODULE$.println((new StringBuilder(0)).append(prefix()).append(name).append(suffix()).toString());
  }
}

public class DefaultMixinSample extends BaseMixinSample implements TraitA, TraitB {
  public void B() {
    TraitB.B$(this);
  }
  
  public void A() {
    TraitA.A$(this);
  }
  
  public DefaultMixinSample() {
    super("Hello", "!");
    TraitA.$init$(this);
    TraitB.$init$(this);
  }
  
  public void greet(String name) {
    super.greet(name);
  }
  
  public static void test() {
    DefaultMixinSample$.MODULE$.test();
  }
}

public interface TraitA {
  static void $init$(TraitA $this) {}
  
  default void A() {
    Predef$.MODULE$.println("A");
  }
  
  String a();
}

public interface TraitB {
  static void $init$(TraitB $this) {}
  
  default void B() {
    Predef$.MODULE$.println("B");
  }
  
  String b();
}

Apparently, defining a field in a trait implicitly creates a method in the interface that is equivalent to the field name. So, it seems that the trait is realized by defining the actual state of the method that acquires the field in the class of the synthesis destination.

Finally

The purpose of writing this article was to understand how Scala works, but Java programmers may find Scala a little better! I came to think that it would be an opportunity for people to think. It's a language with a long history, but I think it's a very attractive language because it has many new features and features that Java programmers want to see.

I hope to create an opportunity to re-examine Scala from a Java perspective in the same way as I continue to study.

Recommended Posts

Introduction to Scala from a Java perspective, decompiled and understood (basic)
Introduction to Scala from a Java perspective (Class-Tuple edition) to decompile and understand
How to write Scala from the perspective of Java
[Introduction to Java] How to write a Java program
Introduction to monitoring from Java Touching Prometheus
[Introduction to Java] Variable declarations and types
[Java] Introduction to Java
Introduction to java
Connect to Aurora (MySQL) from a Java application
To become a VB.net programmer from a Java shop
Create Scala Seq from Java, make Scala Seq a Java List
Convert Java enum enums and JSON to and from Jackson
Changes from Java 8 to Java 11
Sum from Java_1 to 100
How to jump from Eclipse Java to a SQL file
Java language from the perspective of Kotlin and C #
6 features I missed after returning to Java from Scala
From Java to Ruby !!
[Java] How to erase a specific character from a character string
Introduction to java command
Introduction to Effective java by practicing and learning (Builder pattern)
Assign a Java8 lambda expression to a variable and reuse it
I want to make a list with kotlin and java!
[Java] How to convert a character string from String type to byte type
I want to make a function with kotlin and java!
How to store a string from ArrayList to String in Java (Personal)
How to develop and register a Sota app in Java
ClassCastException occurs when migrating from Java7 to Java8 ~ Generics and overload ~
Take a look at Kotlin from an Effective Java perspective
Migration from Cobol to JAVA
New features from Java7 to Java8
Connect from Java to PostgreSQL
[Java] Introduction to Stream API
[Introduction to rock-paper-scissors games] Java
Convert Excel to Blob with java, save it, read it from DB and output it as a file!
[Introduction to Java] Variables and types (variable declaration, initialization, data type)
Connecting to a database with Java (Part 1) Maybe the basic method
Notes on building Kotlin development environment and migrating from Java to Kotlin
From fledgling Java (3 years) to Node.js (4 years). And the impression of returning to Java
[Swift5] How to communicate from ViewController to Model and pass a value
[Java] How to convert from String to Path type and get the path
Introduction to Java that can be understood even with Krillin (Part 1)
[Introduction to Java] About lambda expressions
[Java] Basic types and instruction notes
[Introduction to Java] About Stream API
How to make a Java container
Introduction to Functional Programming (Java, Javascript)
Use JDBC with Java and Scala.
Run a batch file from Java
Access Teradata from a Java application
[Java] How to create a folder
From introduction to use of ActiveHash
Java to be involved from today
From Java to VB.NET-Writing Contrast Memo-
Reading and writing Java basic files
Introduction to Ruby (from other languages)
Initial introduction to Mac (Java engineer)
Java, interface to start from beginner
From introduction to usage of byebug
Java basic data types and reference types
The road from JavaScript to Java