Guia para interfaces Kotlin
1. Visão geral
Neste tutorial,we’ll discuss how to define and implement interfaces in Kotlin.
Também daremos uma olhada em como várias interfaces podem ser implementadas por uma classe. Isso certamente pode causar conflitos, e aprenderemos o mecanismo que Kotlin possui para resolvê-los.
2. Interfaces em Kotlin
Uma interface é uma maneira de fornecer uma descrição ou contrato para classes na programação orientada a objetos. Eles podem conter propriedades e funções de maneira abstrata ou concreta, dependendo da linguagem de programação. Veremos os detalhes das interfaces em Kotlin.
As interfaces no Kotlin são semelhantes às interfaces em muitas outras linguagens como Java. Mas eles têm uma sintaxe específica, vamos revisá-los nas próximas subseções.
2.1. Definindo Interfaces
Vamos começar definindo nossa primeira interface em Kotlin:
interface SimpleInterface
Esta é a interface mais simples que está completamente vazia. These are also known as marker interfaces.
Vamos agora adicionar algumas funções à nossa interface:
interface SimpleInterface {
fun firstMethod(): String
fun secondMethod(): String {
return("Hello, World!")
}
}
Adicionamos dois métodos à nossa interface definida anteriormente:
-
Um deles chamado firstMethod é um método abstrato
-
Enquanto o outro chamado secondMethod tem uma implementação padrão.
Vamos continuar e adicionar algumas propriedades à nossa interface agora:
interface SimpleInterface {
val firstProp: String
val secondProp: String
get() = "Second Property"
fun firstMethod(): String
fun secondMethod(): String {
return("Hello, from: " + secondProp)
}
}
Aqui nós adicionamos duas propriedades à nossa interface:
-
Um deles é chamadofirstProp is do tipo String e é abstrato
-
O segundo chamadosecondProp também é do tipo string, mas define uma implementação para seu acessador.
Observe queproperties in an interface cannot maintain state. Portanto, o seguinte é uma expressão ilegal no Kotlin:
interface SimpleInterface {
val firstProp: String = "First Property" // Illegal declaration
}
2.2. Implementando interfaces
Agora que definimos uma interface básica, vamos ver como podemos implementá-la em uma classe em Kotlin:
class SimpleClass: SimpleInterface {
override val firstProp: String = "First Property"
override fun firstMethod(): String {
return("Hello, from: " + firstProp)
}
}
Observe que, quando definimosSimpleClass como uma implementação deSimpleInterface,we only have to provide the implementation for abstract properties and functions. No entanto, nóscan override any previously defined property or function, too.
Vamos agora substituir todas as propriedades e funções definidas anteriormente em nossa classe:
class SimpleClass: SimpleInterface {
override val firstProp: String = "First Property"
override val secondProp: String
get() = "Second Property, Overridden!"
override fun firstMethod(): String {
return("Hello, from: " + firstProp)
}
override fun secondMethod(): String {
return("Hello, from: " + secondProp + firstProp)
}
}
Aqui, substituímos a propriedadesecondPrope a funçãosecondFunction que foram definidas anteriormente na interfaceSimpleInterface.
2.3 Implementing Interfaces through Delegation
Delegação é um padrão de design na programação orientada a objetosto achieve code reusability through composition instead of inheritance. Embora seja possível implementar em muitas linguagens, como Java,Kotlin has native support for implementation through delegation.
Se começarmos com uma interface e classe básicas:
interface MyInterface {
fun someMethod(): String
}
class MyClass() : MyInterface {
override fun someMethod(): String {
return("Hello, World!")
}
}
Até agora, nada de novo. Mas agora, podemos definir outra classe que implementaMyInterface através da delegação:
class MyDerivedClass(myInterface: MyInterface) : MyInterface by myInterface
MyDerivedClass espera um delegado como um argumento que realmente implementa a interfaceMyInterface.
Vamos ver como podemos chamar uma função da interface por meio de delegado:
val myClass = MyClass()
MyDerivedClass(myClass).someMethod()
Aqui, instanciamosMyClass e usamos isso como delegado para chamar funções da interface emMyDerivedClass,, que na verdade nunca implementou essas funções diretamente.
3. Herança múltipla
A herança múltipla é um conceito-chave no paradigma de programação orientada a objetos. This allows for a class to inherit characteristics from more than one parent object, like an interface, for example.
Embora isso ofereça mais flexibilidade na modelagem de objetos, ele vem com seu próprio conjunto de complexidades. Um deles é o "problema do diamante".
Java 8 has its own mechanisms for addressing the diamond problem, como qualquer outra linguagem que permite herança múltipla.
Vamos ver como Kotlin trata disso por meio de interfaces.
3.1. Herdando várias interfaces
Começaremos definindo duas interfaces simples:
interface FirstInterface {
fun someMethod(): String
fun anotherMethod(): String {
return("Hello, from anotherMethod in FirstInterface")
}
}
interface SecondInterface {
fun someMethod(): String {
return("Hello, from someMethod in SecondInterface")
}
fun anotherMethod(): String {
return("Hello, from anotherMethod in SecondInterface")
}
}
Observe que ambas as interfaces possuem métodos com o mesmo contrato.
Agora vamos definir uma classe que herda de ambas as interfaces:
class SomeClass: FirstInterface, SecondInterface {
override fun someMethod(): String {
return("Hello, from someMethod in SomeClass")
}
override fun anotherMethod(): String {
return("Hello, from anotherMethod in SomeClass")
}
}
Como podemos ver,SomeClass implementaFirstInterfaceeSecondInterface. Embora sintaticamente isso seja bastante simples, há um pouco de semântica que requer atenção aqui. Abordaremos isso na próxima subseção.
3.2. Resolvendo Conflitos
Ao implementar várias interfaces, uma classe pode herdar uma função que possui uma implementação padrão para o mesmo contrato em várias interfaces. Isso levanta o problema de chamada para esta função a partir de uma instância da classe de implementação.
To resolve this conflict, Kotlin requires the subclass to provide an overridden implementation for such functions to make the resolution explicit.
Por exemplo,SomeClass acima implementaanotherMethod. Mas, se não, Kotlin não saberia se invocarFirst orSecondInterface’s implementação padrão deanotherMethod. SomeClassmust implement anotherMethod for this reason.
No entanto,someMethod é um pouco diferente, pois na verdade não há conflito. FirstInterface não fornece uma implementação padrão parasomeMethod. Dito isso,SomeClass ainda deve implementá-lo porqueKotlin forces us to implement all inherited functions, no matter if they are defined once or multiple times in parent interfaces.
3.3. Resolvendo o problema do diamante
Um "problema de diamante" ocorre quando dois objetos filhos de um objeto base descrevem um comportamento específico definido pelo objeto base. Agora, um objeto herdado desses dois objetos filhos precisa resolver qual comportamento herdado ele assina.
A solução de Kotlin para este problema é através das regras definidas para herança múltipla na subseção anterior. Vamos definir algumas interfaces e uma classe de implementação para apresentar o problema do diamante:
interface BaseInterface {
fun someMethod(): String
}
interface FirstChildInterface: BaseInterface {
override fun someMethod(): String {
return("Hello, from someMethod in FirstChildInterface")
}
}
interface SecondChildInterface: BaseInterface {
override fun someMethod(): String {
return("Hello, from someMethod in SecondChildInterface")
}
}
class ChildClass: FirstChildInterface, SecondChildInterface {
override fun someMethod(): String {
return super.someMethod()
}
}
Aqui definimosBaseInterface que declarou uma função abstrata chamadasomeMethod. Ambas as interfacesFirstChildInterfaceeSecondChildInterface herdam deBaseInterfacee implementam a funçãosomeMethod.
Agora, enquanto implementamosChildClass herdando deFirstChildInterfaceeSecondChildInterface, é necessário substituir a funçãosomeMethod. No entanto,even though we must override the method, we can still simply call super as we do here with SecondChildInterface.
4. Interfaces comparadas a classes abstratas em Kotlin
As classes abstratas em Kotlin sãoclasses which cannot be instantiated. Isso pode conter uma ou mais propriedades e funções. Essas propriedades e funções podem ser abstratas ou concretas. Qualquer classe herdada de uma classe abstrata deve implementar todas as propriedades e funções abstratas herdadas, a menos que a própria classe também seja declarada como abstrata.
4.1. Diferenças entre Interface e Classe Abstrata
Esperar! Isso não soa exatamente como o que uma interface faz?
Na verdade, desde o início, uma classe abstrata não é muito diferente da interface. Mas existem diferenças sutis que governam a escolha que fazemos:
-
Uma classe no Kotlin pode implementar quantas interfaces quiser, mas só pode se estender de uma classe abstrata
-
As propriedades na interface não podem manter o estado, enquanto podem em uma classe abstrata
4.2. Quando devemos usar o quê?
Uma interface é apenas um modelo para definir classes, elas também podem ter algumas implementações padrão. Por outro lado, uma classe abstrata é uma implementação incompleta que é concluída pelas classes estendidas.
Normalmenteinterfaces should be used to define the contract, que elicia os recursos que promete entregar. Uma classe de implementação tem a responsabilidade de cumprir essas promessas. Umabstract class, however, should be used to share partial characteristics with extending classes. Uma classe de extensão pode levar ainda mais longe para completá-la.
5. Comparação com interfaces Java
Com as mudanças na interface Java em Java 8,they have come very close to Kotlin interfaces. Um de nossos artigos anteriores capturathe new features introduced in Java 8 including changes to the interface.
Atualmente, existem principalmente diferenças sintáticas entre as interfaces Java e Kotlin. Uma diferença que se destaca está relacionada à palavra-chave "substituir". No Kotlin, ao implementar propriedades abstratas ou funções herdadas de uma interface, é obrigatório qualificá-las com a palavra-chave “override“. Não existe esse requisito explícito em Java.
6. Conclusão
Neste tutorial, discutimos as interfaces Kotlin, como defini-las e implementá-las. Depois, falamos sobre a herança de várias interfaces e o conflito que elas podem criar. Examinamos como Kotlin lida com esses conflitos.
Finalmente, discutimos interfaces em comparação com classes abstratas no Kotlin. Também falamos brevemente sobre como a interface Kotlin se compara à interface Java.
Como sempre, o código dos exemplos está disponívelover on GitHub.