2013-01-14 4 views
18

Ich brauchte zwei Instanzen, die Zugriff auf andere Privates hat. Natürlich dachte ich an ein Companion-Objekt, das Zugriff auf eine einzige Instanz der Companion-Klasse gewährt. Die Klasse selbst wurde privat gemacht, sodass Benutzer nicht einfach Instanzen mit new erstellen können.Companion-Objekt zu einer privaten Klasse: Warum ist es nicht gültig?

object A { 
    def apply = dual 
    lazy val dual = new A 
} 

private class A { 
    //some irrelevant logic... 
} 

Dieser Code wird nicht kompiliert. Ich bekomme: Klasse A entgeht seinem Definitionsbereich als Teil des Typs A Fehler, die ich nicht wirklich verstehe. Meine aktuelle Problemumgehung war, eine Eigenschaft mit jeder Methodendeklaration zu definieren, die die Klasse haben sollte, und class A diese Eigenschaft zu erweitern, während dual vom Merkmalstyp ist und nicht class A Typ.

Was ist das theoretische Problem, das ich hier vermisse? Warum ist das verboten?

+8

hehehe Zugang zu jedem anderen privaten –

+2

@ AK4749 gut, schließlich ist es ihr Begleiter wir reden. Es ist nicht so, dass die Klasse es _anyone_ erlaubt, auf ihre Privatsphäre zuzugreifen ... Obwohl auf _reflection _... –

Antwort

29

Paolo's Lösung ist gut (+1), aber er hat die Fehlermeldung nicht erklärt, also lass mich das versuchen. Das Problem rührt von der Tatsache her, dass jede Methode einen Rückgabetyp benötigt. Ihre ursprüngliche Definition von apply und dual hat ein Objekt class A zurückgegeben, daher war der implizite Rückgabetyp von beiden A. Das bedeutet, dass A für Clients sichtbar sein muss - wie sonst könnten sie die Funktion aufrufen oder auf die val zugreifen? Da beide - und auch ihr Elternobjekt - öffentlich sind, sind sie global sichtbar. Sie haben jedoch A private deklariert, was bedeutet, dass es außerhalb seines Pakets nicht sichtbar sein muss. Es gibt also einen Konflikt, der vom Compiler nicht gelöst werden kann.

Die allgemeine Regel ist, dass alle Parameter und der Rückgabetyp von Funktionen/Membern (mindestens) denselben Sichtbarkeitsbereich haben müssen wie das verweisende Member selbst *. Eine triviale Möglichkeit, dieses Problem zu lösen, wäre daher, die Sichtbarkeit von und dual auf private zu reduzieren. Das würde den Compiler zufrieden stellen, aber nicht Sie :-)

Ihre Lösung löst das Problem, indem Sie den statischen Rückgabetyp in eine public-Eigenschaft ändern, die somit dieselbe Sichtbarkeit wie die darauf bezogenen Member hat. Der dynamische Typ des zurückgegebenen Objekts ist immer noch class A, dies muss jedoch für Clients nicht sichtbar sein. Dies ist ein klassisches Beispiel für das Prinzip "program to interfaces, not implementations".

Beachten Sie, dass dieses Prinzip in vollem Umfang anzuwenden, könnte man class A in eine private innere Klasse von object A drehen, damit es macht innaccessible auch für andere Klassen im gleichen Paket:

trait A { 
    //... 
} 

object A { 
    def apply: A = dual 
    lazy val dual: A = new AImpl 

    private class AImpl extends A { 
     //some irrelevant logic... 
    } 

} 

* zu pedantisch sein, das umgebende Klasse/Objekt kann die Sichtbarkeit ihrer Mitglieder reduzieren, wie hier:

private class Holder { 
    def member = new Hidden 
} 

private class Hidden 

wo member ist public aber seine einschließende Klasse ist private, effektiv versteckt seine Mitglieder aus der externen Welt. Der Compiler gibt also hier keine Beschwerden ab.

+0

das ist eine sehr gute Antwort –

+0

in der Tat! eine sehr gute Antwort ..! Vielen Dank! –

23

Ich denke, Sie wollen keine private Klasse, sondern eine Klasse mit einem privaten Konstruktor.

class A private() 
object A { 
    def apply = dual 
    lazy val dual = new A 
} 

Jetzt ist Ihre Klasse ist „sichtbar“ außerhalb Code, sondern nur Ihre Begleiter Objektinstanzen davon erstellen.

+0

danke! alberne mich ... manchmal sind die rätselhaftesten Probleme eigentlich irgendwie albern und einfach. –