privado [esto] vs privado

111

En Scala veo una característica como variable privada de objeto. De mi experiencia en Java no muy rica, aprendí a cerrar todo (hacerlo privado) y abrir (proporcionar accesos) si es necesario. Scala introduce un modificador de acceso aún más estricto. ¿Debería usarlo siempre por defecto? ¿O debería usarlo solo en algunos casos específicos donde necesito restringir explícitamente el cambio de valor de campo incluso para objetos de la misma clase? En otras palabras, ¿cómo debo elegir entre

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

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

El segundo es más estricto y me gusta, pero ¿debo usarlo siempre o solo si tengo una razón de peso?

EDITADO: Como veo aquí private[this] es solo un subcase y en lugar de thispuedo usar otros modificadores: "paquete, clase u objeto singleton". Así que lo dejo para algún caso especial.

Esotérico
fuente

Respuestas:

59

No creo que importe demasiado, ya que cualquier cambio solo afectará a una clase de cualquier manera. Así que la razón más importante para preferir privatesobre protectedmás publicno se aplica.

Úselo private[this]donde el rendimiento realmente importa (ya que obtendrá acceso directo al campo en lugar de métodos de esta manera). De lo contrario, simplemente se asientan en un estilo para que la gente no tiene que averiguar por qué esta propiedad es privatey que uno es private[this].

Alexey Romanov
fuente
6
@ om-nom-nom En realidad, no hay mucho que contar. JIT debe incorporar las llamadas al método de acceso generadas por de privatetodos modos, por lo que el impacto debe ser cero o al menos muy muy pequeño.
Alexey Romanov
9
Esta respuesta es engañosa, el motivo real es la variación del sitio de declaración (consulte esta respuesta: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav
1
@AndreyBreslav No estoy de acuerdo con que esa sea la razón. Sí, existe tal caso, pero como dice la respuesta, es bastante raro.
Alexey Romanov
3
Hmm. La respuesta de Marek Adamek a continuación parece ser la verdadera razón para elegir privado [esto] sobre privado. La intención es limitar el acceso a una instancia específica, a diferencia de todas las instancias de la clase.
Ram Rajamony
3
@AlexeyRomanov - la pregunta es "¿Debería usarlo siempre por defecto?". Creo que podría mejorar su respuesta diciendo que private [this] no se puede usar si necesita el campo de otra instancia de la misma clase.
Ram Rajamony
130

Hay un caso en el que private[this]se requiere compilar el código. Esto tiene que ver con una interacción de notación de varianza y variables mutables. Considere la siguiente clase (inútil):

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

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

Entonces, esta clase está diseñada para contener un valor opcional, devolverlo como una opción y permitir que el usuario llame makeEmptypara borrar el valor (de ahí la var). Como se dijo, esto es inútil excepto para demostrar el punto.

Si intenta compilar este código con en privatelugar de private[this], fallará con el siguiente mensaje de error:

error: el tipo covariante T aparece en posición contravariante en el tipo Opción [T] de valor valor_ = clase Titular [+ T] (valor inicial: Opción [T]) {

Este error se produce porque el valor es una variable mutable en el tipo covariante T (+ T) que normalmente es un problema a menos que se marque como privado para la instancia con private[this]. El compilador tiene un manejo especial en su verificación de varianza para manejar este caso especial.

Entonces es esotérico, pero hay un caso en el que private[this]se requiere un cambio private.

Denis Phillips
fuente
1
Puedo ver por qué falla cuando hay mutabilidad en la mezcla, pero ¿por qué obtengo el mismo error cuando nada es mutable ?
Matt Kantor
34

private var namees accesible desde cualquier método de class Dummy(y su compañero object Dummy).

private[this] var namees accesible desde métodos de thisobjeto solamente, no desde otros objetos de class Dummy.

comonad
fuente
18

private [this] (equivalente a protected [this]) significa que "y" solo es visible para los métodos en la misma instancia. Por ejemplo, no puede hacer referencia a y en una segunda instancia en un método igual, es decir, "this.y == that.y" generaría un error de compilación en "that.y". (fuente)

para que pueda hacer esto en privado cada vez que lo desee, pero puede tener algún problema si necesita referirlo

Pben
fuente
13
private[this]no es igual a protected[this]. protected[this]permite que las instancias de subclase accedan al miembro.
drexin
Puede hacerlo this.y == that.yusando ni privado ni privado [esto], acabo de probar ambos
lisak
12

Esto se probó con scala 2.11.5. Considere el código a continuación

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

se compilará y funcionará como este código 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

sin embargo, si usa el modificador '[this]', el código siguiente no se compilará

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
  }
}

Esto se debe a que en el primer caso, 'x' es accesible a nivel de clase, mientras que en el segundo caso es más estricto a nivel de instancia. Significa que solo se puede acceder a 'x' desde la instancia a la que pertenece. Entonces 'this.x' está bien pero 'other.x' no lo está.

Puede consultar la sección 13.5 del libro "Programación en Scala: una guía completa paso a paso" para obtener más detalles sobre los modificadores de acceso.

Marek Adamek
fuente
1
La pregunta no es qué private[this]significa. Tenga en cuenta la primera oración.
Alexey Romanov
9

Al agregar el alcance al modificador privado ( privado [X] ), se comporta efectivamente como una X "hasta", donde X designa algún paquete, clase u objeto singleton que lo incluye.

Por ejemplo, private [bar] , donde bar es un paquete, significa que cada instancia de cada clase que pertenece al paquete bar puede acceder a cualquier miembro que el modificador esté restringiendo.

En el caso de privado [esto] , significa que el miembro solo es accesible para cada instancia. Esto se vuelve más claro en el siguiente ejemplo:

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

Como puede ver, el segundo Foo no tiene ningún problema ya que cualquier instancia puede acceder al val privado i. Sin embargo, para el primer Foo hay un error ya que cada instancia no puede ver la i de otra instancia.

Es una buena práctica escribir en privado [esto] ya que impone una restricción mayor.

Humoyun Ahmad
fuente
6

En la mayoría de los lenguajes de programación OOP como Java, los campos / métodos privados significan que estos campos / métodos privados no son accesibles fuera de la clase. Sin embargo, las instancias / objetos de la misma clase pueden tener acceso a los campos privados de los objetos mediante el operador de asignación o mediante el constructor de copias. En Scala, privado [this] es un objeto privado, lo que asegura que cualquier otro objeto de la misma clase no pueda acceder a los miembros privados [this].

Ejemplo

1.Sin privado [esto]

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.Usando privado [esto]

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

  }
}

Por lo tanto, privado [esto] asegura que el campo _password solo sea accesible con esto.

rafiquenazir
fuente
Ésta es, con mucho, la respuesta más clara y objetiva.
Lucas Lima
2

Para profundizar en el tema del rendimiento que ha mencionado Alexey Romanov, aquí están algunas de mis conjeturas. Citas del libro "Programación en Scala: una guía completa paso a paso, segunda edición" Sección 18.2:

En Scala, cada var que es miembro no privado de algún objeto define implícitamente un método getter y setter con él.

Para probarlo, este código provocará un error de compilación:

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

Scala se queja error: ambiguous reference to overloaded definition. Agregar la palabra clave override a data_=no ayudará debería probar que el método es generado por el compilador. Agregar una privatepalabra clave a una variable dataseguirá causando este error de compilación. Sin embargo, el siguiente código se compila bien:

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

Entonces, supongo private[this]que evitará que scala genere métodos getter y setter. Por lo tanto, acceder a dicha variable ahorrará la sobrecarga de llamar al método getter y setter.

DXDXY
fuente
1

¿Debería usarlo siempre por defecto? ¿O debería usarlo solo en algunos casos específicos donde necesito restringir explícitamente el cambio de valor de campo incluso para objetos de la misma clase? En otras palabras, ¿cómo debo elegir entre

Es mejor usarlo private[this]si planea sincronizar la variable.

Aquí un buen ejemplo de la guía de estilo scala del equipo 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
fuente