Introduction à Scala du point de vue Java (basique)

Cet article est une communauté technique interne de Fujitsu Systems Web Technology, "Innovation Promotion Community" Ceci est l'article sur le 14ème jour du calendrier de l'Avent des vacances d'été d'Inobeko 2020, qui est prévu par "Inobeko" pour faire court. Le contenu de cet article est ma propre opinion et ne représente pas l'organisation à laquelle j'appartiens. Promesse à ce jour: wink:

introduction

Dans cet article, un programmeur Java qui a démarré Scala recompile le fichier de classe généré par la compilation de Scala. J'ai commencé à penser que je voulais comprendre Scala d'un point de vue Java.

Je voudrais procéder avec le flux de base comme suit.

C'est tout.

Parlons un peu du monde de Scala. Cette fois, je voudrais aborder le monde de Scala en utilisant l'édition "de base" de TOUR OF SCALA. (Il y a quelques problèmes de développement)

classe

Les classes dans scala ont presque la même apparence que Java, mais avec des différences dans les paramètres du constructeur. Voyons également comment implémenter la surcharge en fonction d'un exemple.

Exemple de code

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

Après la décompilation

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

Il semble qu'il stocke les méta-informations lors de sa compilation. Il y avait un article dans qiita qui confirmait comment mettre en œuvre le scellé. https://qiita.com/ocadaruma/items/93818bd1a5318e71f0d8

Je ne sais pas si c'est quelque chose que vous voulez savoir, mais c'est pour ça. .. ..

Arguments du constructeur

Les arguments «prefix: String, suffix: String» ont été définis comme champs «private final».

En guise de test, si vous écrivez le paramètre constructeur en tant que paramètre var, ce sera comme suit.

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());
  }
}

Vous pouvez voir que le final a disparu et que la méthode getter (prefix (), suffix ()) et la méthode setter ($ eq) sont générées. Lorsque vous appelez à partir du code scala, il semble que vous puissiez obtenir et définir le préfixe normalement, mais lorsque vous appelez à partir du code Java, cela semble être field name_ $ eq. C'est compliqué ...

méthode de salutation

Dans la méthode greet, les chaînes ont été concaténées avec la méthode + (dans scala, + est également traitée comme une méthode!). Cependant, lorsque je regarde le résultat de la compilation inversée, j'utilise StringBuilder pour concaténer les chaînes. Quoi! Intelligent! Si vous allez combiner des chaînes dans du code Java et le traiter, il ne fait aucun doute que cela sera écrit dans le tableau de révision ** "Veuillez utiliser StringBuilder: angry:", mais dans le cas de Scala c'est Ne t'inquiète pas.

Le résultat de la réécriture en Java et de la décompilation est le suivant.

JavaGreeeter.java


//Code précompilé
//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);
  }
}

Il semble qu'il soit conçu pour combiner simplement des chaînes. Peut-être que Scala traite également tous les opérateurs comme des fonctions. J'imagine que la méthode + de String elle-même est probablement en train d'utiliser StringBuilder (peut-être que scalac est censé l'interpréter avec StringBuilder ...).

Même si vous utilisez des littéraux, il semble que StringBuilder sera utilisé de la même manière.

  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());
    }
   */

objet

Vient ensuite ʻobject`. Ce n'est pas le java.lang.Object de Java. L '«objet» de Scala correspond-il au modèle Singleton en Java? La syntaxe est la suivante.

Exemple de code

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

Après la décompilation

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();
  }
}

Quoi! La classe anonyme est prête.

Probablement, il se compose d'un objet Singleton ʻIdFactory $ et d'une classe ʻIdFactory qui l'appelle. Y a-t-il une raison d'être séparé? C'est la fin de ce temps, pensant qu'il y a quelque chose à faire dans mon étude. Le document officiel indique également que plus tard sera traité en détail, donc j'attends cela avec impatience.

trait

Qu'est-ce qu'un trait? C'est comme une interface, mais il semble qu'elle puisse avoir des champs, être implémentée par défaut ou hériter (mixin) de plusieurs traits.

Ici, créons un trait et créons une classe héritée pour voir quel type de classe est créé.

Exemple de 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)
  }
}

Après la décompilation

Cette fois, un total de 3 fichiers de classe ont été créés. Dans le cas de Java, il n'est pas possible de créer plusieurs classes publiques dans un même code source Java, il a donc été divisé en plusieurs.

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());
  }
}

le trait est maintenant l'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);
  }
}

C'est sous la forme d'appeler l'implémentation par défaut de l'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());
  }
}

Il semble qu'il remplace simplement l'implémentation implémentée. Eh bien, c'est prévu.

mixin

Scala vous permet de synthétiser plusieurs classes en utilisant mixin. Java ne peut pas hériter de plusieurs classes ou interfaces, il peut hériter d'une seule classe parente.

Exemple de code utilisant 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"
}

Après la décompilation


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();
}

Apparemment, définir un champ dans un trait crée implicitement une méthode dans l'interface qui équivaut au nom du champ. Ainsi, il semble que le trait se réalise en définissant l'état réel de la méthode qui acquiert le champ dans la classe de la destination de synthèse.

finalement

Le but de la rédaction de l'article était de comprendre le fonctionnement de Scala, mais les programmeurs Java pourraient trouver Scala un peu mieux! J'en suis venu à penser que ce serait l'occasion pour les gens de réfléchir. C'est un langage avec une longue histoire, mais je pense que c'est un langage très attrayant car il a de nombreuses nouvelles fonctionnalités que les programmeurs Java veulent voir.

J'espère créer une opportunité de reconsidérer Scala dans une perspective Java de la même manière que je continue à étudier.

Recommended Posts

Introduction à Scala du point de vue Java (basique)
Comment écrire Scala du point de vue de Java
[Introduction à Java] Comment écrire un programme Java
Introduction à la surveillance à partir de Java Touching Prometheus
[Introduction à Java] À propos des déclarations et des types de variables
[Java] Introduction à Java
Introduction à Java
Connectez-vous à Aurora (MySQL) depuis une application Java
Pour devenir programmeur VB.net depuis une boutique Java
Créer Scala Seq à partir de Java, faire de Scala Seq une liste Java
Convertir l'énumération Java et JSON vers et depuis Jackson
Changements de Java 8 à Java 11
Somme de Java_1 à 100
Comment passer d'Eclipse Java à un fichier SQL
Langage Java du point de vue de Kotlin et C #
6 fonctionnalités que j'ai manquées après mon retour de Scala à Java
De Java à Ruby !!
[Java] Comment effacer un caractère spécifique d'une chaîne de caractères
Introduction à la commande java
Introduction à Effective Java en pratiquant et en apprenant (modèle Builder)
Attribuer des expressions lambda Java8 aux variables et les réutiliser
Je veux faire une liste avec kotlin et java!
Je veux créer une fonction avec kotlin et java!
Comment stocker des chaînes de ArrayList à String en Java (personnel)
Comment développer et enregistrer une application Sota en Java
ClassCastException se produit lors de la migration de Java7 vers Java8 ~ Génériques et surcharge ~
Jetez un œil à Kotlin dans une perspective Java efficace
Migration de Cobol vers JAVA
Nouvelles fonctionnalités de Java7 à Java8
Connectez-vous de Java à PostgreSQL
[Java] Introduction à l'API Stream
[Introduction aux jeux Janken (comme)] Java
Convertissez Excel en Blob avec java, enregistrez-le, lisez-le à partir de DB et exportez-le sous forme de fichier!
[Introduction à Java] À propos des variables et des types (déclaration de variable, initialisation, type de données)
Connexion à une base de données avec Java (partie 1) Peut-être la méthode de base
Remarques sur la création de l'environnement de développement de Kotlin et la migration de Java vers Kotlin
De Java naissant (3 ans) à Node.js (4 ans). Et l'impression de retourner à Java
[Swift5] Comment communiquer de ViewController à Model et transmettre une valeur
[Java] Comment convertir du type String en type Path et obtenir le chemin
[Introduction à Java] À propos des expressions lambda
[Java] Types de base et notes d'instructions
[Introduction à Java] À propos de l'API Stream
Comment créer un conteneur Java
Introduction à la programmation fonctionnelle (Java, Javascript)
Utilisez JDBC avec Java et Scala.
Exécuter le fichier de commandes à partir de Java
Accéder à Teradata depuis une application Java
Java sera impliqué dès aujourd'hui
De Java à VB.NET - Écriture de notes de contraste
Introduction à Ruby (à partir d'autres langues)
Introduction initiale à Mac (ingénieur Java)
Java, interface pour partir du débutant
De l'introduction à l'utilisation de byebug
Types de données de base et types de référence Java
La route de JavaScript à Java