privé [ceci] vs privé

112

Dans Scala, je vois une telle fonctionnalité comme une variable objet-privé. De mon arrière-plan Java pas très riche, j'ai appris à tout fermer (le rendre privé) et à ouvrir (fournir des accesseurs) si nécessaire. Scala introduit un modificateur d'accès encore plus strict. Dois-je toujours l'utiliser par défaut? Ou devrais-je l'utiliser uniquement dans certains cas spécifiques où j'ai besoin de restreindre explicitement la modification de la valeur du champ même pour les objets de la même classe? En d'autres termes, comment choisir entre

class Dummy {
    private var name = "default name"
}

class Dummy {
    private[this] var name = "default name"
}

La seconde est plus stricte et je l'aime bien mais dois-je toujours l'utiliser ou seulement si j'ai une bonne raison?

EDITED: Comme je le vois, voici private[this] juste un sous-cas et au lieu de thisje peux utiliser d'autres modificateurs: "package, classe ou objet singleton". Je vais donc laisser cela pour un cas particulier.

Sotérique
la source

Réponses:

59

Je ne pense pas que cela compte trop, car tout changement ne touchera qu'une seule classe de toute façon. Ainsi , la raison la plus importante à préférer privateplus protectedsur publicne sont pas applicables.

Utilisez private[this]là où les performances comptent vraiment (puisque vous aurez un accès direct au champ au lieu des méthodes de cette façon). Sinon, choisissez un style pour que les gens n'aient pas besoin de comprendre pourquoi cette propriété est privateet celle-private[this].

Alexey Romanov
la source
6
@ om-nom-nom En fait, il n'y a pas grand chose à dire. JIT devrait de toute façon incorporer les appels de méthode d'accesseur générés par private, donc l'impact devrait être nul ou au moins très très petit.
Alexey Romanov
9
Cette réponse est trompeuse, la raison réelle est la variance du site de déclaration (voir cette réponse: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav
1
@AndreyBreslav Je ne suis pas d'accord que ce soit la raison. Oui, un tel cas existe mais comme le dit la réponse, il est assez rare.
Alexey Romanov
3
Hmm. La réponse de Marek Adamek ci-dessous semble être la vraie raison de choisir privé [ceci] plutôt que privé. L'intention est de limiter l'accès à une instance spécifique, par opposition à toutes les instances de la classe.
Ram Rajamony
3
@AlexeyRomanov - la question demande "Dois-je toujours l'utiliser par défaut?". Je pense que vous pourriez améliorer votre réponse en disant que private [this] ne peut pas être utilisé si vous avez besoin du champ d'une autre instance de la même classe.
Ram Rajamony
130

Il y a un cas où il private[this]est nécessaire de faire compiler le code. Cela a à voir avec une interaction de la notation de la variance et des variables mutables. Considérez la classe suivante (inutile):

class Holder[+T] (initialValue: Option[T]) {
    // without [this] it will not compile
    private[this] var value = initialValue

    def getValue = value
    def makeEmpty { value = None }
}

Cette classe est donc conçue pour contenir une valeur facultative, la renvoyer en option et permettre à l'utilisateur d'appeler makeEmptypour effacer la valeur (d'où la variable var). Comme indiqué, cela est inutile sauf pour démontrer le point.

Si vous essayez de compiler ce code avec privateau lieu de private[this]cela, le message d'erreur suivant échouera:

erreur: le type de covariant T se produit en position contravariante dans le type Option [T] de valeur value_ = class Holder [+ T] (initialValue: Option [T]) {

Cette erreur se produit car value est une variable mutable sur le type de covariant T (+ T), ce qui pose normalement un problème à moins d'être marqué comme privé pour l'instance avec private[this]. Le compilateur a une gestion spéciale dans sa vérification de variance pour gérer ce cas particulier.

C'est donc ésotérique mais il y a un cas où il private[this]faut plus private.

Denis Phillips
la source
1
Je peux voir pourquoi cela échoue lorsque la mutabilité est dans le mélange, mais pourquoi ai-je la même erreur quand rien n'est mutable ?
Matt Kantor
35

private var nameest accessible depuis n'importe quelle méthode du class Dummy(et de son compagnon object Dummy).

private[this] var nameest accessible à partir des méthodes d' thisobjet uniquement, pas à partir d'autres objets de class Dummy.

comonad
la source
18

private [this] (équivalent à protected [this]) signifie que ce "y" n'est visible que par les méthodes de la même instance. Par exemple, vous ne pouvez pas référencer y sur une seconde instance dans une méthode equals, c'est-à-dire que "this.y == that.y" générerait une erreur de compilation sur "that.y". (la source)

vous pouvez donc le faire en privé à chaque fois que vous le souhaitez, mais vous pouvez avoir un problème si vous avez besoin de le référer

Pben
la source
13
private[this]n'est pas égal à protected[this]. protected[this]permet aux instances de sous-classe d'accéder au membre.
drexin
Vous ne pouvez this.y == that.yutiliser ni privé ni privé [ceci], j'ai juste essayé les deux
lisak
12

Ceci a été testé en utilisant scala 2.11.5. Considérez le code ci-dessous

class C(private val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => x == other.x
    case _ => false
  }
}

println(new C(5) == new C(5)) //true
println(new C(5) == new C(4)) //false

il compilera et fonctionnera comme ce code java (1.8)

class C {
    private int x;

    public C(int x) {
        this.x = x;
    }

    public boolean equals(Object obj) {
        if (obj instanceof C) {
            return ((C) obj).x == x;
        }
        else {
            return false;
        }
    }
}

System.out.println(new C(5).equals(new C(5))); //true
System.out.println(new C(5).equals(new C(4))); //false

cependant si vous utilisez le modificateur '[this]' le code ci-dessous ne compilera pas

class C(private[this] val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => this.x == other.x //problem is here
    case _ => false
  }
}

En effet, dans le premier cas, «x» est accessible au niveau de la classe, alors que dans le second cas, il est plus strict au niveau de l'instance. Cela signifie que «x» n'est accessible qu'à partir de l'instance à laquelle il appartient. Donc 'this.x' est bien mais 'other.x' ne l'est pas.

Vous pouvez vous référer à la section 13.5 du livre «Programmation dans Scala: un guide pas à pas complet» pour plus de détails sur les modificateurs d'accès.

Marek Adamek
la source
1
La question n'est pas de savoir ce que cela private[this]signifie. Notez la première phrase.
Alexey Romanov
9

Lors de l'ajout de la portée au modificateur privé ( private [X] ), il se comporte effectivement comme un X «jusqu'à», où X désigne un package, une classe ou un objet singleton englobant.

Par exemple, private [bar] , où bar est un package signifie que chaque instance de chaque classe appartenant à package bar peut accéder au membre que le modificateur restreint.

Dans le cas de private [this] , cela signifie que le membre n'est accessible que pour chaque instance. Cela devient plus clair dans l'exemple suivant:

class Foo(foo:Foo){  
   private[this] val i = 2
   println(this.i + foo.i)
}

>>error: value i is not a member of Foo

class Foo(foo:Foo){  
    private val i = 2
    println(this.i + foo.i)
}

>>defined class Foo

Comme vous pouvez le voir, le deuxième Foo n'a aucun problème puisque n'importe quelle instance peut accéder au val privé i. Cependant, pour le premier Foo, il y a une erreur car chaque instance ne peut pas voir le i d'une autre instance.

C'est une bonne pratique d'écrire private [ceci] car cela impose une restriction plus importante.

Humoyun Ahmad
la source
6

Dans la plupart des langages de programmation POO comme java, les champs / méthodes privés signifient que ces champs / méthodes privés ne sont pas accessibles en dehors de la classe. Cependant, les instances / objets de la même classe peuvent avoir accès aux champs privés des objets en utilisant l'opérateur d'affectation ou au moyen du constructeur de copie. Dans Scala, private [this] est un objet privé, ce qui garantit qu'aucun autre objet de la même classe ne pourra accéder aux membres private [this].

Exemple

1.Sans privé [ceci]

object ObjectPrivateDemo {

  def main(args: Array[String]) {
    var real = new User("realUserName", "realPassword")
    var guest = new User("dummyUserName", "dummyPassword")
    real.displayUser(guest)

  }
}

class User(val username:String,val password:String) {
  private var _username=username
  private var _password=password



  def displayUser(guest:User){

         println(" guest username="+guest._username+" guest password="+guest._password)
       guest._username= this._username
    guest._password=  this._password
       println(" guest username="+guest._username+" guest password="+guest._password)


  }
}

2.Utiliser privé [this]

class User(val username: String, val password: String) {
  private var _username = username
  private[this] var _password = password



  def displayUser(guest: User) {

    println(this._username)
    println(this._password)

    guest._username = this._username
    // for guest._password it will give this :error  value _password is not member of class User
    guest._password = this._password

  }
}

Par conséquent, private [this] s'assure que le champ _password n'est accessible qu'avec this.

rafiquénazir
la source
C'est de loin la réponse la plus claire et la plus objective.
Lucas Lima
2

Pour élaborer sur le problème de performance mentionné par Alexey Romanov, voici quelques-unes de mes hypothèses. Citations du livre "Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition" Section 18.2:

Dans Scala, chaque var qui est membre non privé d'un objet définit implicitement un getter et une méthode setter avec lui.

Pour le tester, ce code provoquera une erreur de compilation:

class PrivateTest{
  var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Scala se plaint error: ambiguous reference to overloaded definition. L'ajout d'un mot-clé override à data_=n'aidera pas devrait prouver que la méthode est générée par le compilateur. L'ajout d'un privatemot clé à une variable dataprovoquera toujours cette erreur de compilation. Cependant, le code suivant se compile correctement:

class PrivateTest{
  private[this] var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Donc, je suppose que private[this]cela empêchera scala de générer des méthodes getter et setter. Ainsi, accéder à une telle variable économisera la surcharge de l'appel des méthodes getter et setter.

DXDXY
la source
1

Dois-je toujours l'utiliser par défaut? Ou devrais-je l'utiliser uniquement dans certains cas spécifiques où j'ai besoin de restreindre explicitement la modification de la valeur du champ même pour les objets de la même classe? En d'autres termes, comment choisir entre

Il est préférable d'utiliser private[this]si vous prévoyez de synchroniser la variable.

Voici un bon exemple du guide de style scala de l'équipe Spark :

// The following is still unsafe.
class Foo {
  private var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}

// The following is safe.
class Foo {
  private[this] var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}
Andriy Kuba
la source