Para que serve o campo de apoio do Kotlin?

94

Como desenvolvedor Java, o conceito de campo de apoio é um pouco estranho para mim. Dado:

class Sample {
    var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value
    }
}

Para que serve este campo de apoio? Os documentos de Kotlin disseram:

As aulas em Kotlin não podem ter campos. No entanto, às vezes é necessário ter um campo de apoio ao usar acessores personalizados .

Por quê? Qual é a diferença em usar o próprio nome das propriedades dentro do configurador, por exemplo. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
fonte
18
Usar a própria propriedade no setter resultará em uma recursão sem fim, pois atribuir algum valor à propriedade sempre chamará o setter.
funglejunk de
1
@ Strelok meu mal .... Eu estava assumindo que this.counter = valueé o mesmo com o equivalente Java ao ler a documentação de Kotlin.
Yudhistira Arya,
5
Este artigo é para Campo vs Propriedade.
Avinash de

Respostas:

87

Porque, digamos, se você não tiver uma fieldpalavra-chave, não será capaz de definir / obter o valor em get()ou set(value). Ele permite que você acesse o campo de apoio nos acessadores personalizados.

Este é o código Java equivalente ao seu exemplo:

class Sample {
    private int counter = 0;
    public void setCounter(int value) {
        if (value >= 0) setCounter(value);
    }
    public int getCounter() {
        return counter;
    }
}

Aparentemente, isso não é bom, pois o setter é apenas uma recursão infinita em si mesmo, nunca mudando nada. Lembre-se no kotlin sempre que você escrever, foo.bar = valueele será traduzido em uma chamada setter em vez de um PUTFIELD.


EDITAR: Java tem campos, enquanto Kotlin tem propriedades , que é um conceito de nível bastante superior do que campos.

Existem dois tipos de propriedades: uma com um campo de apoio, outra sem.

Uma propriedade com um campo de apoio armazenará o valor na forma de um campo. Esse campo possibilita o armazenamento de valor na memória. Um exemplo de tal propriedade são as propriedades firste seconddePair . Essa propriedade mudará a representação na memória de Pair.

Uma propriedade sem um campo de apoio terá que armazenar seu valor de outras maneiras além de armazená-lo diretamente na memória. Deve ser calculado a partir de outras propriedades ou do próprio objeto. Um exemplo de tal propriedade é a indicespropriedade de extensão de List, que não é apoiada por um campo, mas um resultado calculado com base na sizepropriedade. Portanto, isso não mudará a representação na memória List(o que não pode ser feito porque o Java é estaticamente tipado).

glee8e
fonte
Obrigado pela resposta! Meu mal ... eu estava assumindo que this.counter = valueé o mesmo com java equivalente.
Yudhistira Arya,
Em algum lugar documentado? Obrigado :)
Alston
@Alston, aqui está a documentação: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Muitas explicações confusas. Por que não podemos simplesmente dizer que a fieldé mais como um ponteiro ou uma referência a uma variável de membro existente. Como o get/setimediatamente segue counter, portanto, a fieldpalavra-chave é uma referência a counter. Direito?
eigenfield
@typelogic, esta resposta é mais adequada para programadores com experiência em Java / JS (naquela época não havia Kotlin / Native), não C / C ++. O que você acha difuso é pão com manteiga para algumas outras pessoas.
glee8e
34

Inicialmente, eu também tive dificuldade em entender esse conceito. Portanto, deixe-me explicar com a ajuda de um exemplo.

Considere esta aula de Kotlin

class DummyClass {
    var size = 0;
    var isEmpty
        get() = size == 0
        set(value) {
            size = size * 2
        }
}

Agora, quando olhamos para o código, podemos ver que ele tem 2 propriedades, isto é - size(com acessores padrão) e isEmpty(com acessores personalizados). Mas tem apenas 1 campo iesize . Para entender que ele possui apenas 1 campo, vamos ver o equivalente Java dessa classe.

Vá para Ferramentas -> Kotlin -> Mostrar Kotlin ByteCode no Android Studio. Clique em Decompile.

   public final class DummyClass {
   private int size;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.size == 0;
   }

   public final void setEmpty(boolean value) {
      this.size *= 2;
   }
}

Claramente, podemos ver que a classe java tem apenas funções getter e setter para isEmpty , e não há nenhum campo declarado para ela. Da mesma forma, em Kotlin, não há campo de apoio para a propriedade isEmpty, já que a propriedade não depende desse campo. Portanto, não há campo de apoio.


Agora vamos remover o getter e setter personalizados de isEmpty propriedade.

class DummyClass {
    var size = 0;
    var isEmpty = false
}

E o equivalente em Java da classe acima é

public final class DummyClass {
   private int size;
   private boolean isEmpty;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.isEmpty;
   }

   public final void setEmpty(boolean var1) {
      this.isEmpty = var1;
   }
}

Aqui vemos os campos sizee isEmpty. isEmptyé um campo de apoio porque o getter e o setter da isEmptypropriedade dependem dele.

o passageiro escuro
fonte
4
Boa explicação. Obrigado
Sonu Sanjeev,
2
Realmente, obrigado pela explicação. Também vim para Kotlin vindo de Java, e o conceito de propriedades é novo para mim. Mas eu entendi, graças a você e aos guias. :)
Yamashiro Rion
1
Deus pode te abençoar.
Andrea Cioccarelli
1
Eu gosto dessa resposta, ela é citada honestamente os fatos. Ainda estou em dúvida, porque C # não precisa de fieldpalavra-chave, é possível que um aprimoramento de linguagem de Kotlin remova essa fieldpalavra-chave estranha e evite que as almas indefesas caiam no abismo da recursão infinita?
eigenfield
9

Os campos de apoio são bons para executar a validação ou disparar eventos na mudança de estado. Pense nas vezes em que você adicionou código a um setter / getter Java. Os campos de apoio seriam úteis em cenários semelhantes. Você usaria campos de apoio quando precisasse controlar ou ter visibilidade sobre setters / getters.

Ao atribuir o campo com o próprio nome do campo, você está, na verdade, chamando o configurador (ou seja set(value)). No exemplo que você tem, this.counter = valueiria recursivamente em set (valor) até que estourássemos nossa pilha. O uso fieldignora o código setter (ou getter).

Mark Mucha
fonte
Desculpe, mas sua explicação contém o termo que precisa ser explicado. E então, primeiro você citou um cenário Java e, de repente, sem avisar, você mudou o lains para uma instrução Kotlin real. A necessidade de Kotlin para a palavra field- chave não está em C #, portanto, precisamos de uma explicação melhor do que a que você citou aqui.
eigenfield
2

Meu entendimento é usar o identificador de campo como uma referência ao valor da propriedade em get ou set , quando você deseja alterar ou usar o valor da propriedade em get ou set .

Por exemplo:

class A{
    var a:Int=1
        get(){return field * 2}    // Similiar to Java: public int geta(){return this.a * 2}
        set(value) {field = value + 1}
}

Então:

var t = A()
println(t.a)    // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2         // The real action is similar to Java code: t.a = t.a +1
println(t.a)    // OUTPUT: 6, equal to Java code: println(t.a * 2)
Freddie
fonte
0

A terminologia backing fieldé cheia de mistério. A palavra-chave usada é field. Os get/setmétodos seguem imediatamente ao lado da variável de membro que está prestes a ser obtida ou definida por meio desse mecanismo de métodos de proteção de porta. A fieldpalavra-chave se refere apenas à variável de membro que deve ser definida ou obtida . No momento, Kotlin, você não pode se referir à variável de membro diretamente dentro dos métodos de porta de proteção get ou set porque, infelizmente, resultará em recursão infinita porque irá re-invocar get ou set e, assim, conduzir o tempo de execução para o abismo profundo.

No entanto, em C # , você pode referenciar diretamente a variável de membro dentro dos métodos getter / setter. Estou citando esta comparação para apresentar a ideia de que esta fieldpalavra-chave é como o Kotlin atual a está implementando, mas espero que seja removida em versões posteriores e nos permita fazer referência direta à variável de membro sem resultar em recursão infinita.

campo próprio
fonte