2016-06-11 13 views
10

Ho appena scoperto un comportamento piuttosto strano dell'oscilloscopio Scala quando il codice bytecode generato dal codice Scala viene utilizzato dal codice Java. Si consideri il seguente frammento di codice usando Spark (Spark 1.4, Hadoop 2.6):Scope pacchetto-privato in Scala visibile da 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())); 
    } 
} 

Questo codice non funziona, che si prevede come io distruggo volontariamente un Broadcast prima di usarlo, ma la cosa è che nel mio modello mentale non dovrebbe anche compilare, figuriamoci correre bene.

Infatti, Broadcast.destroy(Boolean) è dichiarato come private[spark] quindi non dovrebbe essere visibile dal mio codice. Proverò a guardare il bytecode di Broadcast ma non è la mia specialità, è per questo che preferisco postare questa domanda. Inoltre, scusate, ero troppo pigro per creare un esempio che non dipendesse da Spark, ma almeno avete avuto l'idea. Nota che posso usare vari metodi di Spark per i pacchetti privati, non si tratta solo di Broadcast.

Qualche idea di cosa sta succedendo?

risposta

15

Se ricostruiamo questo problema con un esempio più semplice:

package yuvie 

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

E decompilare questo in Java:

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

vediamo che private[package] in Scala diventa public in Java. Perché? Questo deriva dal fatto che il pacchetto privato Java non è equivalente al pacchetto privato Scala. C'è una bella spiegazione in this post:

La distinzione importante è che 'privata [mypackage]' a Scala è non Java pacchetto-privato, per quanto si sembra simile. I pacchetti Scala sono veramente gerarchici e 'private [mypackage]' concede l'accesso alle classi e agli oggetti fino a "mypackage" (compresi tutti i pacchetti gerarchici che possono essere compresi tra). (Non ho il riferimento spec Scala per questo e il mio sottovalutazione qui può essere confusa, sono utilizzando [4] come riferimento.) Pacchetti di Java sono non gerarchica, e sovvenzioni pacchetto-privato accesso solo alle classi in quel pacchetto, come pure come sottoclassi della classe originale, qualcosa che il 'privato [mypackage] di Scala non consente.

Quindi, "pacchetto [mypackage]" è sia sempre più restrittivo che Java pacchetto-privato. Per entrambi i motivi, JVM package-private non può essere utilizzato per implementarlo e l'unica opzione che consente gli usi che Scala espone nel compilatore è "pubblico".

+0

Grazie per la risposta. Non pensi che questo sia un po 'pericoloso per gli scrittori API? Funzionalità che non volevano mai essere esposte finiscono chiaramente visibili da Java. Mi chiedo se potrebbero usare qualche trucco di annotazione per generare avvertimenti sull'utente quando provano a usare un membro che doveva essere privato – Dici

+0

@Dici Se hai intenzione di interop con Java, allora sì, penso che sia qualcosa che devi prendere in esame, soprattutto se questo espone gli interni che non si desidera che i client invochino. Anche se in questo caso particolare, potresti anche chiamare il pubblico 'Broadcast.metodo destory', sparandosi al piede in modo equivalente. –

+0

Sì, quello che intendevo è che ora che conosco tutti gli interni di Spark dichiarati come package-private come esposti tramite l'API Java, penso che probabilmente ci dovrebbero essere più wrapper Java per nascondere funzionalità che non erano destinate a essere pubbliche. Il mio esempio è stato solo per mostrare che il metodo è stato effettivamente chiamato. – Dici