Orientação a objetos em Kotlin
Open Source Your Knowledge, Become a Contributor
Technology knowledge has to be shared and made accessible for free. Join the movement.
Funções Getter e Setter
Getters e setters são usados para obter valores de propriedades de classe e definir valores de propriedades de classe.
No capítulo anterior, vimos que as propriedades declaradas dentro de uma classe são acessadas usando o operador .
.
Se tivermos uma classe Empregado com a seguinte definição:
class Empregado{
var salario: Int = 0
var nome: String = ""
}
Podemos criar um objeto da classe Empregado e acessar as propriedades usando o operador .
dessa forma:
Sempre que tentamos obter ou definir o valor de uma propriedade, as propriedades não são acessadas diretamente. As funções getter e setter são chamadas para obter e definir o valor de uma propriedade.
O que são getters e setters?
Getter e Setter são as funções que são geradas por padrão para cada propriedade de classe pelo Kotlin. Eles são usados para acessar a propriedade.
Se tentarmos chamar NomeDaClasse.propriedade = valor
a função set()
é chamada internamente e se tentarmos obter o valor da propriedade usando NomeDaClasse.propriedade
a função get()
é chamada internamente.
Por que a função Getter e Setter?
É uma boa prática não expor as variáveis de uma classe fora dela. Se você vem de Java, deve ter declarado propriedades de classe como privadas e criado métodos getter e setter para ela. Kotlin por padrão fornece esse recurso.
Getter e Setter
As funções get()
e set()
padrão se parecem com:
class Pessoa{
var nome: String = ""
get() = field
set(value) {
field = value
}
val idade: Int = 0
get() = field
}
Propriedades declaradas com var
têm funções get()
e set()
. Mas as propriedades declaradas com val
têm apenas a função get()
.
Isso ocorre porque os valores de val
não podem ser reatribuídos.
A palavra-chave field
é usada para representar a variável. O value
é usado para representar o valor a ser atribuído à variável.
Pode ser alterado também. Essas funções get()
e set()
são redundantes, pois o Kotlin as fornece por padrão.
Definir getters e setters personalizados
Também podemos fornecer funções get()
e set()
personalizadas:
Aqui criamos funções get()
e set()
personalizadas para nome
e função get()
personalizada para idade
.
Vamos criar o objeto desta classe e imprimir a saída:
fun main() {
val person = Person()
person.name = "Ninja" // calls set() function
println("A idade de ${person.name} é ${person.age}") // calls get() functions
}
Modificadores de acesso
Modificadores de acesso ou modificadores de visibilidade são usados para definir o escopo de uma classe, função, campo, interface etc. As palavras-chave do modificador de visibilidade restringem o uso de classes, funções, propriedades etc e informa se estão disponíveis em suas subclasses, em outros arquivos, classes, outros pacotes ou não.
No Kotlin, os modificadores de visibilidade podem ser aplicados a classes, construtores, objetos, interfaces, funções, propriedades e seus setters. Os getters têm a mesma visibilidade que a propriedade.
Existem quatro modificadores de visibilidade no Kotlin:
private
protected
internal
public
O modificador de visibilidade padrão é public. Isso significa que se não especificamos nenhum modificador de visibilidade para uma classe, função, propriedade etc, por padrão o compilador irá considerá-lo como público. Os modificadores de visibilidade têm significados diferentes de acordo com o local onde são colocados. Vamos olhar para eles um por um.
Modificadores de visibilidade em campos de nível superior
Campos de nível superior são aqueles que são colocados fora das classes ou interfaces. Eles estão presentes diretamente dentro de qualquer arquivo Kotlin.
Para campos de nível superior, os diferentes modificadores de visibilidade significam:
public
- É um modificador padrão e pode ser omitido. Isso significa que as declarações são visíveis em todos os lugares. Eles podem ser usados em diferentes arquivos, classes, pacotes, etc.internal
- Declarações marcadas como internas são visíveis dentro do mesmo módulo. Um módulo significa o conjunto de arquivos Kotlin compilados juntos. Durante o desenvolvimento do projeto podemos ter vários módulos dentro do mesmo projeto. Outros módulos podem ser uma biblioteca ou aplicativo auxiliar que suporta o aplicativo principal.private
- As declarações marcadas comprivate
estão disponíveis apenas dentro do mesmo arquivo Kotlin. Eles não podem ser usados fora desse arquivo .kt.
O modificador protected
não está disponível para campos de nível superior.
Modificadores de visibilidade dentro de classes e interfaces
As funções, propriedades, objetos dentro de uma classe têm acesso a modificadores. Observe que a visibilidade de toda a classe ou interface depende de seu modificador de visibilidade que está no nível superior.
Os modificadores de visibilidade dentro das classes significam:
public
- Quem pode ver a classe também pode ver toda a sua declaração pública. Isso significa que as declarações públicas são visíveis em todos os lugares. Eles são visíveis em outros arquivos, classes, pacotes e até módulos.internal
- Igual aos campos de nível superior, eles são visíveis dentro do mesmo módulo.protected
- As declarações marcadas comprotected
são visíveis apenas dentro da classe e sua subclasse. Eles não são visíveis fora da classe. Isso significa que uma função marcada com a palavra-chaveprotected
não pode ser usada fora da classe ou de sua subclasse.private
- As declarações marcadas com palavra-chaveprivate
são visíveis apenas dentro dessa classe. Eles não são visíveis fora da classe, nem mesmo nas subclasses.
Vejamos um exemplo de modificadores de visibilidade em classes:
Herança Kotlin
O que é herança?
A herança é uma das características mais importantes da programação orientada a objetos. Ele permite que uma classe herde características de outra classe.
Suponha que você criou uma classe Vehicle que possui propriedades e métodos relacionados a veículos. Agora precisamos criar outra classe Car que tenha muitas propriedades comuns aos veículos. Então, em vez de reescrever o mesmo código, podemos fazer a classe Car herdar a classe Vehicle. Da mesma forma, podemos criar uma classe TwoWheeler que novamente pode herdar algumas propriedades comuns básicas da classe Vehicle e pode ter algumas propriedades próprias.
Dessa forma, a Herança pode ser usada para dividir o código adequadamente, permitindo a reutilização do código e fornecendo mais estrutura ao código.
A herança permite que a nova classe herde propriedades e métodos de uma classe existente. A classe existente ou a classe da qual a nova classe é herdada é conhecida como classe base ou classe pai ou superclasse. A nova classe que herda as características da classe existente é conhecida como classe derivada ou classe filha ou subclasse.
Herança em Kotlin
Todas as classes em Kotlin são por padrão final
. Isso significa que essas classes não são herdáveis.
Para tornar uma classe herdável, adicionamos uma palavra-chave open
no cabeçalho da classe.
Uma classe filha herda uma classe pai usando o operador :
. A sintaxe para herdar uma classe é:
open class ClassePai{
// Características da ClassePai
}
class ClasseFilha : ClassePai(){
// Características da ClasseFilha
}
Vamos criar um exemplo bem básico de herança:
Neste exemplo, marcamos a classe Vehicle como aberta (open
) para torná-la herdável.
A classe Car herda a classe Vehicle e, portanto, herda sua função run().
Em Kotlin, uma classe pode herdar apenas uma classe, que é igual a Java. Assim, Kotlin não permite herança múltipla. Mas uma classe pode implementar muitas interfaces.
Construtor primário na herança
Se a classe pai tiver um construtor primário, a classe filha deverá chamar o construtor primário da classe pai. No exemplo acima, na classe Car também chamamos o construtor primário da classe Vehicle (o construtor primário foi criado pelo compilador, pois não fornecemos um). Vamos passar propriedades para a classe pai e ver como elas funcionam.
Neste exemplo, passamos valores ao criar o objeto da classe filha. A partir dessas propriedades, passamos carryPassenger, tyres e fuelCapacity para o construtor primário da classe pai Vehicle .
Ao criar o objeto da classe Car, o construtor primário (padrão) da classe filha que é Car é chamado, que chama internamente o construtor primário da classe pai Vehicle primeiro e depois seu bloco init
.
Depois disso, o construtor padrão primário da classe filha Car é concluído e, em seguida, o bloco init
da classe filha Car é executado.
Construtor secundário na herança
Se a classe base não tiver um construtor primário, mas tiver um construtor secundário, a classe derivada deverá chamar o construtor secundário da classe base usando a palavra-chave super
.
Neste exemplo, o construtor secundário de Truckclass chamou o construtor secundário de Vehicleclass usando a palavra-chave super. Todos os argumentos necessários para a classe pai são fornecidos durante a chamada super.
Sobreposição de métodos (polimorfismo)
Na herança, podemos fornecer implementação específica dos métodos da classe base dentro da classe derivada. Isso significa que se existe uma função na classe pai, podemos fornecer uma implementação diferente da mesma função na classe filha. É conhecido como sobreposição de métodos.
No Kotlin, os métodos também são final
por padrão. Para sobrepor um método, ele deve ser marcada como open
(assim como a classe) na classe pai e na classe filha devemos usar a palavra-chave override em vez de open para especificar que substituímos a função.
O nome da função e os parâmetros devem ser os mesmos .
E o tipo de retorno das funções de classe filha pode ser um subtipo das funções de classe pai.
Vejamos um exemplo no qual substituiremos dois métodos da classe base:
Neste exemplo, sobrescrevemos dois métodos run() e drivenBy() de Vehicleclasse e os sobrescrevemos em Jeepclasse.
Também podemos chamar funções da classe pai dentro da classe filha usando a palavra-chave super.
Propriedades de sobreposição
Da mesma forma, também podemos sobrepor as propriedades e alterar seu valor dentro da classe filha. As propriedades devem ser marcadas open para substituí-las e usar a palavra-chave override na classe filha quando substituirmos a propriedade, assim como fizemos para métodos de classe.