Question

Dans une question précédente, Accès scala.None de Java , il semble que les gens avaient utilisé javap pour savoir comment scala.None d'accès de Java. Je voudrais savoir comment ils ont fait cela. Pour votre information, la réponse est:

scala.Option$.MODULE$.apply(null);

qui peut être court-circuité à:

scala.Option.apply(null);

Compte tenu de ce programme (OptionTest.scala):

object OptionTest extends App {
  val x = scala.None
  val y = scala.Some("asdf")
}

J'ai couru sur javap comme ceci:

javap -s -c -l -private OptionTest

Ceci est une partie de la sortie de javap:

public static final scala.None$ x();
  Signature: ()Lscala/None$;
  Code:
   0:   getstatic  #11; //Field OptionTest$.MODULE$:LOptionTest$;
   3:   invokevirtual  #55; //Method OptionTest$.x:()Lscala/None$;
   6:   areturn

J'ai aussi couru javap sur scala.None et scala.Option. Comment serait un chiffre à partir de la sortie javap que:

  1. None est un seul objet de type None.type qui se prolonge Option
  2. La méthode apply() pour l'objet compagnon est requis

Était-ce utile?

La solution

Il existe des règles comment le code Scala est compilé à JVM-bytecode. En raison du nom potentiel heurte le code généré est pas toujours intuitive à comprendre, mais si les règles sont connues, il est possible d'avoir accès au code Scala compilé au sein de Java.

Attention: Tout en écrivant, je remarque que javac et eclipse-javac se comportent différemment dans l'accès du code Scala de Java. Il est possible que le code ci-dessous avec l'un de la compilation mais pas avec eux l'autre.

Classes, Constructors, Méthodes

Il n'y a pas de règles particulières ici. Ce qui suit la classe Scala

class X(i: Int) {
  def m1 = i*2
  def m2(a: Int)(b: Int) = a*b
  def m3(a: Int)(implicit b: Int) = a*b
}

est accessible comme une classe Java normale. Il est compilé dans un fichier nommé X.class:

X x = new X(7);
x.m1();
x.m2(3, 5);
x.m3(3, 5);

Avis, que pour les méthodes sans parameterList un parameterList vide est créé. parameterlists multiples sont fusionnés à un seul.

Les champs, les valeurs

Pour un getters class X(var i: Int) de classe et Setters sont créés. Pour une class X(val i: Int) classe seulement Getter est créé:

//Scala
val x = new X(5)
x.i = 3 // Setter
x.i // Getter

//Java
X x = new X(5);
x.i_$eq(3); // Setter
x.i(); // Getter

Notez que dans Java un identifiant ne peut pas inclure des signes spéciaux. Par conséquent scalac génère pour chacun de ces signes spéciaux un nom spécifique. Il y a une classe scala.reflect.NameTransformer qui peut coder / décoder les opérations:

scala> import scala.reflect.NameTransformer._
import scala.reflect.NameTransformer._

scala> val ops = "~=<>!#%^&|*/+-:\\?@"
ops: String = ~=<>!#%^&|*/+-:\?@

scala> ops map { o => o -> encode(o.toString) } foreach println
(~,$tilde)
(=,$eq)
(<,$less)
(>,$greater)
(!,$bang)
(#,$hash)
(%,$percent)
(^,$up)
(&,$amp)
(|,$bar)
(*,$times)
(/,$div)
(+,$plus)
(-,$minus)
(:,$colon)
(\,$bslash)
(?,$qmark)
(@,$at)

Une classe class X { var i = 5 } est traduit par le même schéma que lorsque le champ est créé dans le constructeur. Accès direct à la i variable de Java n'est pas possible, car il est privé.

Objets

Il n'y a pas une telle chose comme un objet Scala en Java. Par conséquent, scalac doit faire de la magie. Pour un object X { val i = 5 } objet deux fichiers de classe JVM sont générés: X.class et X$.class. Le premier fonctionne comme une interface, elle comprend des procédés statiques à des champs d'accès et les méthodes de l'objet Scala. Ce dernier est une classe singleton qui ne peut pas être instancié. Il a un champ qui contient l'instance singleton de la classe, MODULE$ nommé, qui permet d'accéder au singleton:

X.i();
X$.MODULE$.i();

Classes de cas

Le compilateur Scala génère automatiquement une méthode pour appliquer une classe de cas et Getters pour les champs. La classe de cas case class X(i: Int) est facilement accessible:

new X(3).i();
X$.MODULE$.apply(3);

caractères

Un trait trait T { def m }, qui ne contient que des membres abstraits, est compilé à une interface, qui est placé dans un fichier de classe nommé T.class. Par conséquent, il peut facilement mis en œuvre par une classe Java:

class X implements T {
  public void m() {
    // do stuff here
  }
}

Si le trait contient des éléments en béton il y a un fichier de classe nommé <trait_name>$class.class généré, en plus de l'interface normale. Le trait

trait T {
  def m1
  def m2 = 5
}

peut également facilement mis en œuvre dans Java. Le fichier de classe T$class.class contient les éléments en béton du trait, mais il semble qu'ils sont impossibles d'accès de Java. Ni javac ni l'éclipse-javac compileront un accès à cette classe.

Un peu plus de détails sur la façon dont les traits sont compilés sont disponibles .

Fonctions

littéraux de fonction sont compilés comme des instances anonymes des classes fonction personnaliséeN. Un objet Scala

object X {
  val f: Int => Int = i => i*2
  def g: Int => Int = i => i*2
  def h: Int => Int => Int = a => b => a*b
  def i: Int => Int => Int = a => {
    def j: Int => Int = b => a*b
    j
  }
}

est compilé les fichiers de classe normale, comme décrit ci-dessus. En outre, chaque fonction obtient littéral son propre fichier de classe. Ainsi, pour les valeurs de la fonction d'un fichier de classe nommée <class_name>$$anonfun$<N>.class est générée, où N est un nombre continu. Pour les méthodes de fonction (méthodes, qui renvoient une fonction) un fichier de classe nommé <class_name>$$anonfun$<method_name>$<N>.class est généré. Les parties du nom de la fonction sont séparés par des signes de dollar et en face de l'identificateur de anonfun il y a aussi deux signes dollar. Pourfonctions imbriquées le nom de la fonction imbriquée est ajoutée à la fonction externe, cela signifie une fonction intérieure obtiendra un fichier de classe comme <class_name>$$anonfun$<outer_method_name>$<N>$$anonfun$<inner_method_name>$<N>.class. Lorsqu'une fonction intérieure ne porte pas de nom, comme on le voit dans h il obtient le nom apply.

Ce moyen dans notre cas nous obtenons:

  • X$$anonfun$1.class pour f
  • X$$anonfun$g$1.class pour g
  • X$$anonfun$h$1$$anonfun$apply$1.class pour h
  • X$$anonfun$i$1.class et X$$anonfun$i$1$$anonfun$j$1$1.class pour i et j

Pour y accéder utiliser leur appliquent méthode:

X.f().apply(7);
X.g().apply(7);
X.h().apply(3).apply(5);
X.i().apply(3).apply(5);

Répondez à la question

Vous devez savoir:

  • une classe normale Scala peut accéder par leurs constructeurs ou leurs méthodes apply-
  • quand il n'y a pas de constructeur que il y a une méthode applicable
  • quand il n'y a pas de constructeur et aucune méthode apply que il y a un autre fichier de classe nommé de la même manière la classe est appelée, qui ajoute un signe de dollar à la fin. Rechercher dans cette classe pour un champ MODULE$
  • les constructeurs et apply-méthodes sont héritées, recherche donc les super-classes si vous ne trouvez rien dans les sous-classes

Quelques exemples

Option

// javap scala.Option
public abstract class scala.Option extends java.lang.Object implements ... {
  ...
  public static final scala.Option apply(java.lang.Object);
  public scala.Option();
}

javap dit qu'il a un constructeur et une méthode apply. En outre, il dit que la classe est abstraite. Ainsi, seule la méthode apply peut utiliser:

Option.apply(3);

Certains

// javap scala.Some
public final class scala.Some extends scala.Option implements ... {
  ...
  public scala.Some(java.lang.Object);
}

Il a un constructeur et une méthode apply-(parce que nous savons Option a un et certains étend en option). Utilisez l'un d'entre eux et être heureux:

new Some<Integer>(3);
Some.apply(3);

Aucun

// javap scala.None
public final class scala.None extends java.lang.Object{
  ...
}

Il n'a pas de constructeur, pas applicable méthode et ne couvre pas l'option. Donc, nous allons jeter un coup d'oeil à None$:

// javap -private scala.None$
public final class scala.None$ extends scala.Option implements ... {
  ...
  public static final scala.None$ MODULE$;
  private scala.None$();
}

Ouais! Nous avons trouvé un champ MODULE$ et appliquer méthode de l'option. De plus, nous avons trouvé le constructeur privé:

None$.apply(3) // returns Some(3). Please use the apply-method of Option instead
None$.MODULE$.isDefined(); // returns false
new None$(); // compiler error. constructor not visible

Liste

scala.collection.immutable.List est abstraite, nous avons donc à utiliser scala.collection.immutable.List$. Il a une méthode applicable qui attend un scala.collection.Seq. Donc, pour obtenir une liste il faut d'abord une Seq. Mais si nous regardons Seq il n'y a pas apply-méthode. De plus, lorsque nous regardons les super-classes de Seq et à scala.collection.Seq$ nous ne pouvons trouver une apply-méthodes qui attend une Seq. Alors, quoi faire?

Nous devons regarder comment scalac crée une instance de liste ou Seq. Tout d'abord créer une classe Scala:

class X {
  val xs = List(1, 2, 3)
}

Compile avec scalac et regardez le fichier de classe avec javap:

// javap -c -private X
public class X extends java.lang.Object implements scala.ScalaObject{
...
public X();
  Code:
   0:   aload_0
   1:   invokespecial   #20; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   getstatic   #26; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
   8:   getstatic   #31; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   11:  iconst_3
   12:  newarray int
   14:  dup
   15:  iconst_0
   16:  iconst_1
   17:  iastore
   18:  dup
   19:  iconst_1
   20:  iconst_2
   21:  iastore
   22:  dup
   23:  iconst_2
   24:  iconst_3
   25:  iastore
   26:  invokevirtual   #35; //Method scala/Predef$.wrapIntArray:([I)Lscala/collection/mutable/WrappedArray;
   29:  invokevirtual   #39; //Method scala/collection/immutable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List;
   32:  putfield    #13; //Field xs:Lscala/collection/immutable/List;
   35:  return

}

Le constructeur est intéressant. Il nous apprend que un tableau d'entiers est créé (l. 12) qui est rempli avec 1, 2 et 3. (l. 14-25). Après que cet ensemble est livré à scala.Predef$.wrapIntArray (l. 26). Ce scala.collection.mutable.WrappedArray résultant est livré de nouveau à notre liste (l. 29). A la fin, la liste est mémorisée dans le champ (l. 32). Lorsque nous voulons créer une liste en Java, nous devons faire la même chose:

int[] arr = { 1, 2, 3 };
WrappedArray<Object> warr = Predef$.MODULE$.wrapIntArray(arr);
List$.MODULE$.apply(warr);

// or shorter
List$.MODULE$.apply(Predef$.MODULE$.wrapIntArray(new int[] { 1, 2, 3 }));

Cela semble laid, mais ça fonctionne. Si vous créez une belle bibliothèque à la recherche qui enveloppe l'accès à la bibliothèque Scala, il sera facile à utiliser Scala de Java.

Résumé

Je sais qu'il ya des règles plus comment le code Scala est compilé en bytecode. Mais je pense que les informations ci-dessus, il devrait être possible de trouver ces règles par vous-même.

Autres conseils

Je ne suis pas en concurrence avec l'autre réponse, mais puisque les gens semblent souvent ne pas remarquer cela, vous pouvez le faire dans le rempl.

scala> :paste
// Entering paste mode (ctrl-D to finish)

object OptionTest extends App {
  val x = scala.None
  val y = scala.Some("asdf")
}

// Exiting paste mode, now interpreting.

defined module OptionTest

scala> :javap -v OptionTest$
Compiled from "<console>"
public final class OptionTest$ extends java.lang.Object implements scala.App,scala.ScalaObject
  SourceFile: "<console>"
  Scala: length = 0x

  [lots of output etc]   

  public scala.None$ x();
    Code:
     Stack=1, Locals=1, Args_size=1
     0: aload_0
     1: getfield    #65; //Field x:Lscala/None$;
     4: areturn
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top