privado [este] vs privado

112

No Scala, vejo esse recurso como variável privada do objeto. Com meu conhecimento não muito rico em Java, aprendi a fechar tudo (torná-lo privado) e abrir (fornecer acessadores) se necessário. Scala apresenta um modificador de acesso ainda mais restrito. Devo sempre usá-lo por padrão? Ou devo usá-lo apenas em alguns casos específicos em que preciso restringir explicitamente a alteração do valor do campo, mesmo para objetos da mesma classe? Em outras palavras, como devo escolher entre

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

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

O segundo é mais rígido e gosto dele, mas devo usá-lo sempre ou apenas se tiver um motivo forte?

EDITADO: Como vejo aqui private[this] é apenas um subcaso e em vez de thisposso usar outros modificadores: "pacote, classe ou objeto singleton". Então, vou deixar para algum caso especial.

Sotérico
fonte

Respostas:

59

Eu não acho que isso importe muito, já que qualquer mudança afetará apenas uma classe de qualquer maneira. Assim, a razão mais importante para preferir privatemais protectedlongo publicnão se aplica.

Use private[this]onde o desempenho realmente importa (já que você obterá acesso direto ao campo em vez de métodos desta forma). Caso contrário, escolha apenas um estilo para que as pessoas não precisem descobrir por que essa propriedade é privatee aquela que é private[this].

Alexey Romanov
fonte
6
@ om-nom-nom Na verdade, não há muito o que contar. JIT deve embutir as chamadas de método acessador geradas por privatequalquer maneira, então o impacto deve ser zero ou pelo menos muito pequeno.
Alexey Romanov
9
Essa resposta é enganosa, o motivo real é a variação do site de declaração (veja esta resposta: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav
1
@AndreyBreslav Eu discordo que seja esse o motivo. Sim, existe tal caso, mas como diz a resposta, é muito raro.
Alexey Romanov
3
Hmm. A resposta de Marek Adamek abaixo parece ser a verdadeira razão para escolher privado [este] em vez de privado. A intenção é limitar o acesso a uma instância específica, em oposição a todas as instâncias da classe.
Ram Rajamony
3
@AlexeyRomanov - a pergunta "Devo sempre usá-lo por padrão?". Acho que você poderia melhorar sua resposta dizendo que private [this] não pode ser usado se você precisar do campo de outra instância da mesma classe.
Ram Rajamony
130

Há um caso em que private[this]é necessário fazer a compilação do código. Isso tem a ver com uma interação de notação de variância e variáveis ​​mutáveis. Considere a seguinte classe (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 }
}

Portanto, essa classe é projetada para conter um valor opcional, retorná-lo como uma opção e permitir que o usuário faça uma chamada makeEmptypara limpar o valor (daí o var). Conforme declarado, isso é inútil, exceto para demonstrar o ponto.

Se você tentar compilar este código com em privatevez de, private[this]ele falhará com a seguinte mensagem de erro:

erro: o tipo covariante T ocorre na posição contravariante no tipo Opção [T] de valor valor_ = classe Titular [+ T] (valor inicial: Opção [T]) {

Este erro ocorre porque o valor é uma variável mutável no tipo covariante T (+ T) que normalmente é um problema, a menos que seja marcado como privado para a instância com private[this]. O compilador tem tratamento especial em sua verificação de variância para tratar deste caso especial.

Portanto, é esotérico, mas há um caso em que private[this]é necessário terminar private.

denis phillips
fonte
1
Posso ver por que falha quando há mutabilidade na mistura, mas por que recebo o mesmo erro quando nada é mutável ?
Matt Kantor,
35

private var nameé acessível a partir de qualquer método do class Dummy(e seu companheiro object Dummy).

private[this] var nameé acessível a partir de métodos de thisobjeto apenas, não de outros objetos de class Dummy.

comonad
fonte
18

private [this] (equivalente a protected [this]) significa que aquele "y" só é visível para métodos na mesma instância. Por exemplo, você não poderia fazer referência a y em uma segunda instância em um método equals, ou seja, "this.y == that.y" geraria um erro de compilação em "that.y". (fonte)

então você pode fazer isso de forma privada sempre que quiser, mas você pode ter algum problema se precisar encaminhá-lo

Pben
fonte
13
private[this]não é igual a protected[this]. protected[this]permite que instâncias de subclasse acessem o membro.
drexin
Você pode fazer this.y == that.yusando nem privado nem privado [isto], eu apenas tentei ambos
lisak
12

Isso foi testado usando o scala 2.11.5. Considere o código abaixo

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

ele irá compilar e 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

entretanto, se você usar o modificador '[this]', o código abaixo não 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
  }
}

Isso ocorre porque no primeiro caso 'x' está acessível no nível da classe, enquanto no segundo caso é mais estrito no nível da instância. Isso significa que 'x' pode ser acessado apenas a partir da instância a que pertence. Portanto, 'this.x' está bem, mas 'other.x' não.

Você pode consultar a seção 13.5 do livro "Programação no Scala: um guia passo a passo abrangente" para obter mais detalhes sobre modificadores de acesso.

Marek Adamek
fonte
1
A questão não é perguntar o que private[this]significa. Observe a primeira frase.
Alexey Romanov
9

Ao adicionar o escopo ao modificador privado ( private [X] ), ele efetivamente se comporta como um “até” X, onde X designa algum pacote, classe ou objeto singleton envolvente.

Por exemplo, private [bar] , onde bar é um pacote significa que cada instância de cada classe pertencente ao pacote bar pode acessar qualquer membro que o modificador esteja restringindo.

No caso de private [this] , significa que o membro está acessível apenas para cada instância. Isso fica mais claro no seguinte exemplo:

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 você pode ver, o segundo Foo não tem nenhum problema, pois qualquer instância pode acessar o val i privado. No entanto, para o primeiro Foo, há um erro, pois cada instância não pode ver o i de outra instância.

É uma boa prática escrever privado [this], pois impõe uma restrição maior.

Humoyun Ahmad
fonte
6

Na maioria das linguagens de programação OOP como java, campos / métodos privados significam que esses campos / métodos privados não são acessíveis fora da classe. No entanto, instâncias / objetos da mesma classe podem ter acesso aos campos privados de objetos usando o operador de atribuição ou por meio do construtor de cópia. No Scala, private [this] é o objeto privado, o que garante que qualquer outro objeto da mesma classe não possa acessar membros privados [this].

Exemplo

1.Sem privado [este]

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 [este]

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

  }
}

Portanto, private [this] garante que o campo _password só seja acessível com isso.

Rafiquenazir
fonte
Esta é de longe a resposta mais clara e objetiva.
Lucas Lima
2

Para elaborar o problema de desempenho que Alexey Romanov mencionou, aqui estão alguns de meus palpites. Citações do livro "Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition" Seção 18.2:

No Scala, cada var que é membro não privado de algum objeto define implicitamente um método getter e um método setter com ele.

Para testá-lo, este código causará um erro de compilação:

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

Scala reclama error: ambiguous reference to overloaded definition. Adicionar a palavra-chave override data_=não ajudará a provar que o método é gerado pelo compilador. Adicionar privatepalavra-chave à variável dataainda causará este erro de compilação. No entanto, o código a seguir é compilado corretamente:

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

Portanto, acho private[this]que impedirá que o scala gere métodos getter e setter. Portanto, acessar essa variável irá economizar a sobrecarga de chamar os métodos getter e setter.

DXDXY
fonte
1

Devo sempre usá-lo por padrão? Ou devo usá-lo apenas em alguns casos específicos em que preciso restringir explicitamente a alteração do valor do campo, mesmo para objetos da mesma classe? Em outras palavras, como devo escolher entre

É melhor usar private[this]se você planeja sincronizar a variável.

Aqui está um bom exemplo do guia de estilo scala da equipe 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
fonte