Java <-> Scala interop: transparent Liste et Carte de conversion
Je suis en train d'apprendre Scala et j'ai un projet en Java pour migrer à la Scala. Je veux migrer en réécrivant les classes une par une, et en vérifiant que la nouvelle classe ne rompt pas le projet.
Ce projet Java utilise beaucoup de java.util.List
et java.util.Map
. Dans les nouvelles classes Scala je voudrais utiliser Scala List
et Map
pour avoir bonne mine Scala code.
Le problème est que les nouvelles classes (ceux qui sont wtitten en Scala) de ne pas intégrer seamelessly avec Java code: Java besoins java.util.List
, Scala a besoin de sa propre scala.List
.
Voici un exemple simplifié du problème. Il y a des cours Principal, Logique, Dao. Ils s'appellent les uns les autres dans une ligne: Principal -> Logique -> Dao.
public class Main {
public void a() {
List<Integer> res = new Logic().calculate(Arrays.asList(1, 2, 3, 4, 5));
}
}
public class Logic {
public List<Integer> calculate(List<Integer> ints) {
List<Integer> together = new Dao().getSomeInts();
together.addAll(ints);
return together;
}
}
public class Dao {
public List<Integer> getSomeInts() {
return Arrays.asList(1, 2, 3);
}
}
Dans ma situation, les classes Principal et Dao sont les classes du framework (je n'ai pas besoin de migrer). Classe Logique est la logique de gestion et profitera à beaucoup de Scala fonctionnalités intéressantes.
J'ai besoin de réécrire la classe Logique en Scala, tout en préservant l'intégrité des classes Principal et Dao. Le meilleur de réécriture ressemblerait (ne fonctionne pas):
class Logic2 {
def calculate(ints: List[Integer]) : List[Integer] = {
val together: List[Integer] = new Dao().getSomeInts()
together ++ ints
}
}
Idéal de comportement: les Listes à l'intérieur de Logic2 sont indigènes Scala Listes. Tous les sortir/java.util.Lists
obtenir boxed/"unboxed" automagiquement. Mais cela ne fonctionne pas.
Au lieu de cela, ce n'travail (grâce à scala-javautils (GitHub)):
import org.scala_tools.javautils.Implicits._
class Logic3 {
def calculate(ints: java.util.List[Integer]) : java.util.List[Integer] = {
val together: List[Integer] = new Dao().getSomeInts().toScala
(together ++ ints.toScala).toJava
}
}
Mais il semble laid.
Comment puis-je obtenir transparent magie de conversion de Listes et Cartes entre Java <-> Scala (sans besoin de faire toScala/toJava)?
Si il n'est pas possible, quelles sont les meilleures pratiques en matière de migration Java -> Scala code qui utilise java.util.List
et amis?
Vous devez vous connecter pour publier un commentaire.
Croyez-moi; vous n'avez pas voulez transparent conversion d'avant en arrière. C'est précisément ce que le
scala.collection.jcl.Conversions
fonctions tenté de le faire. Dans la pratique, il provoque beaucoup de maux de tête.La racine du problème avec cette approche est à la Scala va injecter automatiquement les conversions implicites nécessaires pour faire un appel de méthode de travail. Ce qui peut avoir des vraiment des conséquences malheureuses. Par exemple:
Ce code ne serait pas totalement hors de caractère pour quelqu'un qui est nouveau à la Scala collections cadre ou même juste le concept de immuable collections. Malheureusement, il est complètement faux. Le résultat de cette fonction est le même carte. L'appel à
put
déclenche une conversion implicite enjava.util.Map<String, Int>
, qui accepte avec plaisir les nouvelles valeurs et est rapidement écarté. L'originalmap
est pas modifié (tel qu'il est, en effet, immuable).Jorge Ortiz met le mieux quand il a dit que vous ne devrait définir les conversions implicites pour l'une des deux fins:
A
etB
qui ne sont pas liés. Vous pouvez définir une conversionA => B
si et seulement si vous auriez préféré avoirA <: B
(<:
signifie "sous-type").Depuis
java.util.Map
est évidemment pas un nouveau type sans rapport avec quoi que ce soit dans notre hiérarchie, nous ne pouvons pas tomber sous la première condition. Ainsi, notre seul espoir est de notre conversionMap[A, B] => java.util.Map[A, B]
pour se qualifier pour le second. Cependant, il ne fait absolument aucun sens pour ScalaMap
d'hériter dejava.util.Map
. Ils sont vraiment complètement orthogonale interfaces/traits. Comme démontré ci-dessus, tentant d'ignorer ces lignes directrices seront presque toujours bizarre et un comportement inattendu.La vérité est que la javautils
asScala
etasJava
méthodes ont été conçues pour résoudre ce problème exact. Il y a une conversion implicite (un certain nombre d'entre eux en fait) dans javautils deMap[A, B] => RichMap[A, B]
.RichMap
est un tout nouveau type défini par javautils, de sorte que son seul but est d'ajouter des membres àMap
. En particulier, il ajoute laasJava
méthode, qui renvoie un wrapper carte qui met en œuvrejava.util.Map
et les délégués à l'original de votreMap
instance. Cela rend le processus beaucoup plus explicite et beaucoup moins sujettes à erreur.En d'autres termes, en utilisant des
asScala
etasJava
est la meilleure pratique. Après avoir descendu deux de ces routes de façon indépendante dans une application de production, je peux vous dire de première main que le javautils approche est beaucoup plus sûr et plus facile de travailler avec. N'essayez pas de contourner ses protections uniquement pour le plaisir de vous sauver de 8 caractères!import scala.collection.JavaConverters._
Voici quelques exemples d'utilisation de Jorge Ortiz est scalaj de la collection de la bibliothèque:
la javautils projet est disponible à partir de la central repository maven
Avec Scala 2.8, il pourrait être fait comme ceci: