Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Kotlin
Code

Kotlin a Partir do Princípio: Classes e Objetos

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Advanced Functions
Kotlin From Scratch: Advanced Properties and Classes

Portuguese (Português) translation by Luis Drum (you can also view the original English article)

Kotlin é uma linguagem de programação moderna que é compilada em bytecode Java. É gratuita e de código aberto e promete tornar a codificação para Android ainda mais divertida. 

No artigo anterior, você aprendeu usos avançados de funções em Kotlin, tais como funções inline, closures, funções de nível superior e funções de extensão.

Neste post você terá uma introdução à programação orientada a objetos em Kotlin, aprendendo sobre classes: construtores e propriedades, conversão e recursos de classe mais avançados que o Kotlin torna fácil.

1. Classes

Uma classe é uma unidade de programa que agrupa funções e dados para executar algumas tarefas relacionadas. Declaramos uma classe Kotlin usando a palavra-chave class — de modo semelhante ao Java.

O código anterior é a declaração de classe mais simples—  acabamos de criar uma classe vazia chamada Book. Nós ainda podemos instanciar essa classe, mesmo que ela não contenha um corpo, usando seu construtor padrão.

Como se pode observar no código acima, não usamos a palavra-chave new para instanciar essa classe — como é usual em outras linguagens de programação. new não é uma palavra-chave em Kotlin. Isso torna nosso código fonte conciso ao criar uma instância da classe. Mas lembre-se que instanciação de uma classe Kotlin em Java exigirá a palavra-chave new.

Construtores de Classe e Propriedades

Vamos ver como adicionar um construtor e propriedades para nossa classe. Mas primeiro, vamos ver uma classe típica em Java:

Olhando para nossa classe de modelo Book acima, temos o seguinte:

  • dois campos: title e isbn
  • um construtor único
  • getters e setters para os dois campos (felizmente, o IntelliJ IDEA pode nos ajudar a gerar esses métodos)

Agora vamos ver como podemos escrever o código anterior em Kotlin em vez disso:

Uma classe bem arranjada! Agora reduzimos o número de linhas de código de 20 para apenas 9. A função constructor() é chamada de construtor secundário em Kotlin. Esse construtor é equivalente ao construtor Java que chamamos ao instanciar uma classe.

Em Kotlin, não há um conceito de campo como você pode estar familiarizado; em vez disso, ele usa o conceito de "propriedades". Por exemplo, temos duas propriedades (leitura-escrita) mutáveis, declaradas com a palavra-chave var: title e isbn na classe Book. (Se você precisa de uma revisão de variáveis em Kotlin, por favor, visite o primeiro post desta série: Variáveis, Tipos Básicos e Matrizes).

Uma coisa incrível é que os getters e setters para essas propriedades são gerados automaticamente para nós por debaixo dos panos pelo compilador Kotlin. Observe que nós não especificamos quaisquer modificadores de visibilidade para essas propriedades — por padrão, elas são públicas. Em outras palavras, elas podem ser acessadas de qualquer lugar.

Vejamos uma outra versão da mesma classe em Kotlin:

Nesse código nós removemos o construtor secundário. Em vez disso, nós declaramos um construtor no cabeçalho da classe chamado de construtor primário. Um construtor primário não tem qualquer lugar para colocar um bloco de código, então utilizamos o modificador init para inicializar os parâmetros de entrada do construtor primário. Observe que o bloco de código init é executado imediatamente quando a instância da classe é criada.

Como você pode ver, nosso código ainda tem um monte de clichês. Vamos reduzi-lo ainda mais:

Nossa classe Book agora é apenas uma linha de código. Isso é muito legal! Observe que na lista de parâmetros do construtor primário, definimos nossas propriedades mutáveis: title e isbn diretamente dentro do construtor primário com a palavra-chave var.

Podemos também adicionar valores padrão para qualquer uma das propriedades de classe dentro do construtor.

Na verdade, nós também podemos omitir a palavra-chave constructor, mas somente se ele não tem nenhum modificador de visibilidade (public, private ou protected) ou quaisquer anotações.

Uma classe muito elegante, devo dizer!

Agora podemos criar uma instância de classe como esta:

Acessando e Definindo Propriedades

Em Kotlin, podemos obter uma propriedade do objeto da classe Book, seguido de um separador de ponto ., e então do nome da propriedade title. Este estilo conciso de acessar propriedades chama-se sintaxe de acesso a propriedades. Em outras palavras, não temos de chamar o método getter da propriedade para acessar, ou chamar o setter para definir uma propriedade em Kotlin — como fazemos em Java.

Porque a propriedade isbn é declarada com a palavra-chave var (leitura-escrita), também podemos alterar o valor da propriedade usando o operador de atribuição =.

Vejamos outro exemplo:

Aqui, atualizamos o parâmetro isbn para ser imutável (somente leitura) em vez disso — usando a palavra-chave val. Nós instanciamos uma instância de classe book e reatribuímos à propriedade title o valor "Things Fall Apart". Observe que, quando nós tentamos mudar o valor da propriedade isbn para 1234, o compilador reclamou. Isso ocorre porque a propriedade é imutável, tendo sido definida com a palavra-chave val.

Interoperabilidade Java

Esteja ciente de que ao declarar um parâmetro com o modificador var dentro do construtor primário, o compilador de Kotlin (nos bastidores) nos ajudou a gerar ambos os acessadores de propriedade: getter e setter. Se você usar o val, gerará apenas o getter.

Isto significa que os chamadores Java simplesmente podem obter ou definir o campo de propriedade chamando o método getter ou setter da propriedade, respectivamente. Lembre-se, isso depende do modificador usado para definir a propriedade em Kotlin: var ou val.

Setters e Getters Personalizados

Nesta seção mostrarei como criar acessores personalizados(getters e setters) para uma propriedade em Kotlin, se você quiser. Criar um setter personalizado pode ser útil se você deseja validar ou verificar um valor antes dele ser definido como uma propriedade de classe. E um getter de propriedade personalizada pode ser útil quando você deseja alterar ou modificar o valor que deve ser retornado.

Criando um Setter Personalizado 

Porque queremos criar nosso próprio getter ou setter personalizado para uma propriedade, nós temos que definir essa propriedade no corpo da classe em vez do cabeçalho do construtor da classe.

É por isso que mudamos a propriedade mutável title (leitura-escrita) para o corpo da classe e demos a ela um valor padrão (ou então não compilaria).

Você pode ver que definimos nosso próprio método setter set(value) para o title logo abaixo da definição de propriedade — note que você não pode modificar essa assinatura do método set(), porque isto é o que o compilador espera como uma função de setter de propriedade personalizada.

O parâmetro de value passado para o método set representa o valor real que foi atribuído à propriedade pelos usuários — você pode alterar o nome do parâmetro, se quiser, mas value é preferível. Nós validamos value, verificando se o valor é vazio. Se estiver vazio, a execução para e lança uma exceção; caso contrário, transfere o valor para uma variável especial field.

Esta variável especial field dentro do método set é um codinome para o campo de apoio da propriedade — um campo de apoio é apenas um campo que é usado por propriedades quando você deseja modificar ou usar o campo de dados. Ao contrário de value, não é possível renomear essa variável especial field.

Criando um  Getter Personalizado

É muito fácil criar um getter personalizado para uma propriedade em Kotlin.

Dentro do método get, simplesmente retornamos um field modificado — no nosso caso, nós retornamos o  título do livro em letras maiúsculas.

Note que cada vez que definimos um valor para a propriedade title, seu bloco do método set é executado — o mesmo vale para o método get cada vez que o recuperamos.

Se você quer aprender sobre as funções de membro de uma classe Kotlin (o tipo de função que é definida dentro de uma classe, objeto ou interface), visite o post Mais Diversão com Funções nesta série.

Mais Sobre Construtores

Como discutido anteriormente, temos dois tipos de construtores em Kotlin: primário e secundário. Nós temos a liberdade de combinar os dois em uma única classe — como você pode ver no exemplo abaixo:

Note que nós não pode declarar propriedades dentro de um construtor secundário, como fizemos para o construtor primário. Se queremos fazer isso, temos que declará-las dentro do corpo da classe e em seguida, inicializá-las no construtor secundário.

No código acima, definimos o valor padrão da propriedade new para a classe Car (Lembre-se, new não é uma palavra-chave em Kotlin) — podemos usar o construtor secundário para mudá-la se quisermos. Em Kotlin, cada construtor secundário deve chamar o construtor primário, ou chamar outro construtor secundário que chama o construtor primário — usamos a palavra-chave this para alcançar esse objetivo.

Observe também que podemos ter vários construtores secundários dentro de uma classe.

Se uma classe estende uma superclasse, então nós podemos usar a palavra-chave super (semelhante ao Java) para chamar o construtor da superclasse (discutiremos herança em Kotlin em um post futuro).

Como eu disse anteriormente, para nós incluirmos explicitamente um modificador de visibilidade para um construtor de uma classe, temos que incluir a palavra-chave constructor — por padrão, os construtores são públicos.

Aqui, nós fizemos o construtor privado — isso significa que os usuários não podem instanciar um objeto usando seu construtor diretamente. Isso pode ser útil se você deseja que os usuários em vez disso, chamem um outro método (um método de fábrica) para fazer a criação de objetos indiretamente.

2. Tipos Any e Nothing

Em Kotlin, o tipo de nível superior na hierarquia de tipos é chamado Any. Ele é equivalente ao tipo Object em Java. Isto significa que todas as classes em Kotlin herdam explicitamente  do tipo Any, incluindo String, Int, Double, e assim por diante. O tipo Any contém três métodos: equalstoString e hashcode .

Nós também podemos utilizar a classe Nothing em Kotlin em funções que retornam sempre uma exceção — em outras palavras, para funções que não encerram normalmente. Quando uma função retorna Nothing, então nós sabemos que vai lançar uma exceção. Não existe nenhum equivalente deste tipo existe em Java.

Isso pode ser útil ao testar o comportamento de manipulação de erros em seus testes de unidade.

3. Modificadores de Visibilidade

Modificadores de visibilidade nos ajudam a restringir a acessibilidade de nossa API para o público. Nós podemos fornecer modificadores de visibilidade diferentes para nossas classes, interfaces, objetos, métodos ou propriedades. Kotlin nos fornece quatro modificadores de visibilidade:

Público

Este é o padrão, e qualquer classe, função, propriedade, interface ou objeto que possui este modificador pode ser acessada de qualquer lugar.

Privado

Uma função de nível superior, interface ou classe que é declarada como private pode ser acessada apenas dentro do mesmo arquivo.

Qualquer função ou propriedade que é declarada private dentro de uma classe, interface ou objeto só pode ser visível para outros membros da mesma classe, objeto ou interface.

Protegido

O modificador protected só pode ser aplicado a propriedades ou funções dentro de uma classe, interface ou objeto — não pode ser aplicada a funções de nível superior, classes ou interfaces.Propriedades ou funções com este modificador são acessíveis apenas dentro da classe que o define e em qualquer subclasse.

Interno

Em um projeto que tem um módulo (Gradle ou Maven), uma classe, objeto, interface ou a função especificada com o modificador internal declarado dentro desse módulo é acessível unicamente dentro desse módulo.

4 Conversão Inteligente

Conversão significa pegar um objeto de um tipo e convertê-lo em outro tipo de objeto. Por exemplo, em Java, usamos o operador instanceof para determinar se um determinado tipo de objeto é de outro tipo antes de converte-lo.

Como você pode ver, nós verificamos se a instância shape é um Circle, e então temos que converter explicitamente a referência de shape  para Circle de forma que podemos chamar métodos do tipo Circle.

Outra coisa incrível sobre Kotlin é a agilidade de seu compilador quando se trata de conversão. Vamos agora ver uma versão em Kotlin.

Bem legal! O compilador é inteligente para saber que o bloco if será executado somente se o objeto shape é uma instância de Circle — assim o mecanismo de conversão é feito nos bastidores para nós. Nós podemos agora chamar facilmente propriedades ou funções do tipo Circle dentro do bloco if.

Aqui, a última condição após o && no cabeçalho if será chamada somente quando a primeira condição for true. Se shape não é um Circle, então a última condição não será avaliada.

5. Conversão Explícita 

Podemos usar o operador as (ou o operador unsafe cast) para converter explicitamente uma referência de um tipo para outro tipo em Kotlin.

Se a operação de conversão explícita é ilegal, note que será lançada uma ClassCastException. Para evitar que uma exceção seja lançada durante a conversão, nós pode usar o operador safe cast (ou operador nullable cast) as?.

O operador  as? irá tentar a conversão para o tipo pretendido, e retornará null se o valor não puder ser convertido em vez de gerar uma exceção. Lembre-se que um mecanismo semelhante foi discutido na seção de Nulidade no post Nulidade, Laços e Condições  desta série. Leia-o para refrescar a memória.

6. Objetos

Objetos em Kotlin são mais semelhantes aos objetos de JavaScript do que objetos Java. Observe que um objeto em Kotlin não é uma instância de uma classe específica!

Objetos são muito semelhantes às classes. Aqui estão algumas das características dos objetos em Kotlin:

  • Eles podem ter propriedades, métodos e um bloco init.
  • Essas propriedades ou métodos podem ter modificadores de visibilidade.
  • Eles não podem ter construtores (primários ou secundários).
  • Eles podem estender outras classes ou implementar uma interface.

Vamos agora investigar como criar um objeto.

Colocamos a palavra-chave object antes do nome do objeto que queremos criar. Na verdade, estamos criando objetos únicos quando criamos objetos em Kotlin usando object, porque existe apenas uma instância de um objeto. Você vai aprender mais sobre isto quando discutirmos a interoperabilidade de objetos com Java.

Um singleton é um padrão de projeto de software que garante que uma classe tenha somente uma instância e um ponto global de acesso a ela é fornecido por essa classe. Em qualquer momento que várias classes ou clientes solicitarem a classe, receberão a mesma instância da classe. Você pode conferir o meu post sobre o padrão singleton em Java para aprender mais sobre ele.

Você pode acessar o objeto ou singleton em qualquer lugar em seu projeto — desde que você importe o seu pacote.

Se você é um programador Java, isto é como nós normalmente criamos os singletons:

Como você pode ver, usar a construção de object de Kotlin torna mais fácil e conciso criar singletons.

Objetos em Kotlin podem ser utilizados também para criar constantes. Normalmente em Java criamos constantes em uma classe tornando o campo final estático e público como este:

Este código em Java pode ser convertido em Kotlin de forma mais concisa como este:

Aqui nós declaramos a constante APIConstants com uma propriedade baseUrl dentro de um pacote com.chike.kotlin.constants. Nos bastidores, um  membro Java baseUrl privado estático final é criado e inicializado com a sequência de caracteres da URL.

Para usar esta constante em outro pacote Kotlin, simplesmente importe o pacote.

Interoperabilidade Java

Kotlin converte um objeto em uma classe Java final nos bastidores. Essa classe tem um campo estático privado INSTANCE que contém uma única instância (um singleton) da classe. O código a seguir mostra com que simplicidade os usuários podem chamar um objeto Kotlin do Java.

Aqui, uma classe Java chamada Singleton foi gerada com um membro estático público final INSTANCE, incluindo a função pública final myFunc().

Para fazer a função do objeto ou propriedade em Kotlin ser um membro estático da classe Java gerada, usamos a anotação de @JvmStatic. Veja como usá-la:

Aplicando a anotação @JvmStatic em myfunc(), o compilador  transformou-a em uma função estática.

Agora, os callers Java podem chamá-la como uma chamada a um membro estático normal. Observe que usar o campo estático INSTANCE para chamar os membros ainda funcionará.

7. Objetos Companheiros 

Agora que conseguimos entender o que são objetos em Kotlin, vamos mergulhar em um outro tipo de objetos chamado objetos companheiros.

Porque Kotlin não suporta classes estáticas, métodos ou propriedades como as que temos em Java, a equipe do Kotlin forneceu-nos uma alternativa mais poderosa chamada objeto companheiro. Um objeto companheiro é basicamente um objeto que pertence a uma classe - essa classe é conhecida como a classe complementar do objeto. Isto também significa que as características que eu mencionei para objetos também se aplicam a objetos companheiros.

Criando um Objeto Companheiro

Semelhante aos métodos estáticos em Java, um objeto companheiro não é associado a uma instância de classe, mas com a classe em si — por exemplo, um  método estático de fábrica, que tem o trabalho de criar uma instância da classe.

Aqui, nós fizemos o construtor private — isso significa que usuários fora da classe não podem criar uma instância diretamente. Dentro do bloco do nosso objeto companheiro, temos uma função create(), que cria um objeto Person e retorna-o.

Chamar uma Função de Objeto Companheiro

A instanciação de um objeto companion é inerte. Em outras palavras, ele será instanciado somente quando necessário pela primeira vez. A instanciação de um objeto companion acontece quando é criada uma instância da classe companion ou os membros do objeto companion são acessados.

Vamos ver como chamar uma função de objeto companheiro em Kotlin.

Como você pode ver, é como invocar um método estático em Java normamentel. Em outras palavras, nós apenas chamamos a classe e então chamamos o membro. Note que além de funções, também podemos ter propriedades dentro de nosso objeto companheiro.

Observe também que a classe companion tem acesso irrestrito a todas as propriedades e funções declaradas no seu objeto companheiro, enquanto que um objeto companheiro não pode acessar membros de classe. Podemos ter um bloco de código init dentro de um objeto companion — ele é chamado imediatamente quando o objeto companheiro é criado.

O resultado da execução do código acima será:

Lembre-se, pode existir apenas uma única instância de um objeto de classe companion.

Também somos livres para fornecer ao objeto companheiro um nome.

Aqui, nós lhe demos o nome Factory. Podemos então chamá-lo assim em Kotlin:

Este estilo é verboso, por isso é preferível manter o estilo anterior. Mas isso pode ser útil ao chamar uma função ou propriedade de objeto companheiro a partir do Java.

Como eu disse anteriormente, como objetos, objetos companheiros podem também incluir propriedades ou funções, implementar interfaces e até mesmo estender uma classe.

Aqui, temos uma interface PersonFactory, com apenas uma única função create(). Veja o nosso novo objeto companion modificado, que agora implementa essa interface (você vai aprender sobre interfaces e herança em Kotlin em um post mais tarde).

Interoperabilidade Java

Nos bastidores, os objetos companheiros são compilados de forma semelhante à maneira como um objeto Kotlin é compilado. Em nosso caso, duas classes são geradas para nós: uma classe final Person e uma classe interna final  estática Person$Companion.

A classe Person contém um membro estático final chamado Companion — este campo estático é um objeto da classe interna Person$Companion. A classe interna Person$Companion também tem seus próprios membros, e um deles é uma função pública final, chamada create().

Note que não demos um nome ao nosso objeto companheiro, portanto a classe interna estática gerada era Companion. Se tivéssemos dado um nome, então o nome gerado seria o nome que demos em Kotlin.

Aqui, o objeto companheiro em Kotlin não tem nome, então usamos o nome Companion fornecido pelo compilador para os callers de Java chamá-lo.

A anotação @JvmStatic aplicada em um membro de um objeto companheiro funciona da mesma forma como ela funciona para um objeto normal.

Extensões do Objeto Companheiro

Da mesma forma como funções de extensão podem estender a funcionalidade de uma classe, podemos também estender a funcionalidade de um objeto companheiro. (Se você quer relembrar sobre as funções de extensão em Kotlin, visite o tutorial de Funções Avançadas nesta série).

Aqui, definimos uma função de extensão extFunc() no objeto companheiro ClassA.Companion. Em outras palavras, o extfunc() é uma extensão do objeto companheiro. Então podemos chamar a extensão como se fosse uma função de membro (não é!) do objeto companheiro.

Nos bastidores, o compilador irá criar a função utilitária estática extFunc(). O objeto receptor como um argumento para essa função utilitária é Class$Companion.

Conclusão

Neste tutorial, você aprendeu sobre classes básicas e objetos em Kotlin. Cobrimos o seguinte sobre classes:

  • criação de classes
  • construtores
  • propriedades
  • modificadores de visibilidade
  • conversão inteligente
  • conversão explícita

Além disso, você aprendeu sobre como objetos e objetos companheiros em Kotlin podem facilmente substituir seus métodos estáticos, constantes e singletons que você codifica em Java. Mas isso não é tudo! Ainda há mais para aprender sobre classes em Kotlin. No próximo post, mostrarei ainda mais recursos interessantes que Kotlin tem para a programação orientada a objetos. Te vejo em breve!

Para saber mais sobre a linguagem Kotlin, eu recomendo visitar a documentação de Kotlin. Ou confira alguns dos nossos outros posts de desenvolvimento de aplicativos Android aqui no Envato Tuts+!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.