2016-06-11 1 views
10

Je viens de découvrir un comportement assez étrange de scala portée lorsque le code généré à partir du code Scala est utilisé à partir du code Java. Considérons l'extrait suivant à l'aide Spark (Spark 1.4, Hadoop 2.6):Portée privée dans Scala visible à partir de Java

import java.util.Arrays; 
import java.util.List; 

import org.apache.spark.SparkConf; 
import org.apache.spark.api.java.JavaSparkContext; 
import org.apache.spark.broadcast.Broadcast; 

public class Test { 
    public static void main(String[] args) { 
     JavaSparkContext sc = 
      new JavaSparkContext(new SparkConf() 
           .setMaster("local[*]") 
           .setAppName("test")); 

     Broadcast<List<Integer>> broadcast = sc.broadcast(Arrays.asList(1, 2, 3)); 

     broadcast.destroy(true); 

     // fails with java.io.IOException: org.apache.spark.SparkException: 
     // Attempted to use Broadcast(0) after it was destroyed 
     sc.parallelize(Arrays.asList("task1", "task2"), 2) 
      .foreach(x -> System.out.println(broadcast.getValue())); 
    } 
} 

Ce code échoue, ce qui est attendu que je détruis volontairement Broadcast avant de l'utiliser, mais la chose est que dans mon modèle mental il ne devrait pas même compiler, encore moins courir bien.

En effet, Broadcast.destroy(Boolean) est déclaré comme private[spark] donc il ne devrait pas être visible à partir de mon code. Je vais essayer de regarder le bytecode de Broadcast mais ce n'est pas ma spécialité, c'est pourquoi je préfère poster cette question. Aussi, désolé j'étais trop paresseux pour créer un exemple qui ne dépend pas de Spark, mais au moins vous avez l'idée. Notez que je peux utiliser différentes méthodes paquet-privé de Spark, ce n'est pas seulement environ Broadcast.

Une idée de ce qui se passe?

Répondre

15

Si nous reconstruisons cette question avec un exemple plus simple:

package yuvie 

class X { 
    private[yuvie] def destory(d: Boolean) = true 
} 

Et ce décompile en Java:

[[email protected] yuvie]$ javap -p X.class 
Compiled from "X.scala" 
public class yuvie.X { 
    public boolean destory(boolean); 
    public yuvie.X(); 
} 

Nous voyons que dans private[package] Scala devient public en Java. Pourquoi? Cela vient du fait que le paquet privé Java n'est pas équivalent au paquet privé Scala. Il y a une belle explication in this post:

La distinction importante est que 'privé [mypackage]' à Scala est pas Java package-privé, mais beaucoup, il semble comme ça. Scala paquets sont vraiment hiérarchique, et 'private [mypackage]' d'accès aux classes et objets jusqu'à "mypackage" (y compris tous les paquets hiérarchiques qui peuvent être entre). (Je n'ai pas la référence spec Scala pour cela et mon sous-estime ici peut-être brumeux, je suis en utilisant [4] comme référence.) Les paquets de Java sont pas hiérarchique, et subventions de paquet-privé accès uniquement aux cours dans ce paquet, ainsi que en tant que sous-classes de la classe d'origine, quelque chose que Scala's 'private [mypackage]' ne permet pas. Ainsi, 'package [mypackage]' est à la fois plus ou moins restrictif que Java package-private. Pour les deux raisons, JVM package-private ne peut pas être utilisé pour l'implémenter, et la seule option permettant les utilisations que Scala expose dans le compilateur est 'public'.

+0

Merci pour la réponse. Ne pensez-vous pas que c'est un peu dangereux pour les auteurs d'API? Les fonctionnalités qu'ils n'ont jamais voulu être exposés finissent par être clairement visibles depuis Java. Je me demande si ils pourraient utiliser une astuce d'annotation pour générer des avertissements sur l'utilisateur lorsqu'ils essaient d'utiliser un membre qui devait être privé – Dici

+0

@Dici Si vous envisagez d'interopérer avec Java alors oui, je pense vraiment que c'est quelque chose que vous devez prendre à l'étude, surtout si cela expose des internes que vous ne voulez pas que les clients invoquent. Bien que dans ce cas particulier, vous pouvez également appeler le public 'Broadcast.méthode destory', vous tirant dans le pied de manière équivalente. –

+0

Yup, ce que je voulais dire, c'est que maintenant que je connais tous les internes de Spark déclarés comme paquet privé comme exposés par l'API Java, je pense qu'il devrait y avoir plus de wrappers Java pour cacher les fonctionnalités qui n'étaient pas publiques. Mon exemple était juste pour montrer que la méthode était appelée. – Dici