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:
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)
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.
class Greeter(prefix: String, suffix: String) {
def greet(name: String): Unit =
println(prefix + name + suffix)
}
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. .. ..
The arguments prefix: String, suffix: String
have been defined as private final
fields.
val
(cannot be changed) by default.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 ...
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());
}
*/
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.
object IdFactory {
private var counter = 0
def create(): Int = {
counter += 1
counter
}
}
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.
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)
}
}
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.
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"
}
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.
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