Introdução
Continuando aprendizado e descoberta sobre Kotlin. Falaremos sobre Object e Companion Object, interfaces e herança.
Recursos da linguagens observadores e experimentados
Nessa seção, pretendo falar de alguns recursos que pude experimentar durante o curso, e pratiquei em um projeto piloto que está disponível em um repositório no meu github onde pretendo colocar os demos do que ando testando com Kotlin. Esse repositório é o kotlin-lab.
Herança
Tinhamos falado no post post sobre classes, construtores, inicializadores e objetos. Nessa seção falaremos um pouco sobre herança em Kotlin. Toda classe herda de Any (em Java toda classe herda de Object). Se nenhuma relação de herança for feita explicitamente, o compilador automaticamente definirá que aquela classe herda de Any. Any fornece três métodos para todas classes, equals,hashcode e toString. Por padrão, toda classe em Kotlin são final, e elas não podem ser herdadas. Uma classe para ser herdada, ela deve ser marcada com a palavra reservada open.
Se a classe pai possuir um primary constructor, a classe filha deve inicializar o mesmo na configuração da herança. Existem secondary constructors, então pode ser usado a palavra super, conforme exemplo abaixo:
Classe abstrata
Uma classe ou membros podem ser declarados como abstract. Métodos abstratos não tem uma implementação na classe que declara. Não é necessário registrar uma classe abstrata com open.
Sobreescrita de método
Em Kotlin, as coisas precisam ser explicitamente falada para varios recursos, a sobrescrita de métodos é uma dessas coisas. É necessário usar o modificador override para definir uma sobrescrita. É necessário definir através do modificador open quais métodos podem ou não ser sobrescritos, como no exemplo abaixo.
Caso uma classe filha queira definir que suas subclasses não podem sobrescever sua implementação de um método deve ser usado a palavra final.
Em Kotlin, existe a sobrecarga de propriedade, pode ser útil para definição de valores default de inicialização. Os tipos nesse caso devem ser compatíveis. É possível sobrescrever uma variável val com var, mas não é possível fazer o contrário.
Para invocar a implementação da classe pai, existe a palavra super.
Pensando em uma Inner class, é possível usar super@Outer.
Em Kotlin quando existe mais de uma implementação disponível para o mesmo método, o compilador obriga a classe filha fornecer uma implementação concreta para esse método. Isso pode ser resolvido usando qualquer uma das referências em questão.
Object expressions
Em Kotlin é possível criar um objeto com sensíveis modificações da classe de origem sem necessariamente criar uma subclasse explicitamente. É possível fazer isso de duas formas, através das expressões.
Esse recurso é inicializado e executado imediatamente onde aparecer a referência no código.
Object declarations
Esse tipo de recurso cria Singletons de maneira bem simples em Kotlin.
Esse recurso é inicializado de maneira lazy, apenas no primeiro acesso.
Companion Object
Kotlin não possui o recursos static do Java para atributos ou métodos de classes. O que existe é uma forma de você declarar um singleton da classe através do compation object e criar essa estrutura dentro da classe, como nos exemplos abaixo:
Para compatibilidade com Java, é necessário usar a anottation @JvmStatic.
Esse tipo de objeto é inicializado quando a classe que carrega ele é carregada/resolvida. Lembra muito o comportamento da incialização static do Java.
Interfaces
Interfaces podem conter métodos abstratos bem como implementações. As interfaces diferem da classe abstrata por não guardar estado. Elas podem ter propriedades mas essas precisam ser abstratas ou fornecer implementações para os métodos de acesso.
Uma classe pode implementar diversas interfaces.
Uma interface pode herdar de outras interfaces. Apenas as classes são obrigatórias a definir implementação para interfaces, mas elas podem prover implementações.
Esse exemplo ilustra o aspecto da herança e como tratar conflito de sobrescrita de implementação de interfaces:
Delegation
Se você deseja usar em uma classe que já implementou uma determinada interface, é possível usar o recurso de delegation através da palavra by.
Delegated Property
Em Kotlin é possível definir comportamentos complementares para propriedades de uma classe. Nativamente a linguagem oferece a possibilidade de definir Delegates que implementam comportamento para os contextos abaixo:
- lazy: Define um comportamento para o primeiro acesso a propriedade
- observable: Define um comportamento para qualquer mudança nessa propriedade.
- storing: Define um map para guardar a propriedade.
Exemplos:
Lazy
Observable
Storing
Conclusão
Nesse estudo, apresentei alguns recursos do Kotlin que são bastante inovadores para mim que vim da linguagem Java. Espero continuar aprendendo.
Outras Fontes:
- https://kotlinlang.org/docs/reference/classes.html
- https://kotlinlang.org/docs/reference/object-declarations.html
- https://kotlinlang.org/docs/reference/properties.html
- https://kotlinlang.org/docs/tutorials/kotlin-for-py/inheritance.html
- https://kotlinlang.org/docs/reference/delegation.html
- https://kotlinlang.org/docs/reference/interfaces.html
- https://kotlinlang.org/docs/reference/delegated-properties.html