Uma introdução aos traços no Groovy
1. Visão geral
Neste tutorial, vamos explorar o conceito de características emGroovy. Eles foram introduzidos na versão Groovy 2.3.
2. O que são traços?
Traços são componentes reutilizáveis que representam um conjunto de métodos ou comportamentos que podemos usar para estender a funcionalidade de várias classes.
Por este motivo,they’re considered as interfaces, carrying both default implementations and state. All traits are defined using the trait keyword.
3. Métodos
Declarar um método emtrait é semelhante a declarar qualquer método regular em uma classe. No entanto, não podemos declarar métodos protegidos ou privados de pacote emtrait.
Vamos ver como os métodos públicos e privados são implementados.
3.1. Métodos públicos
Para começar, vamos explorar como os métodospublic são implementados em umtrait.
Vamos criar um métodotrait chamadoUserTrait e um métodopublicsayHello:
trait UserTrait {
String sayHello() {
return "Hello!"
}
}
Depois disso, criaremos uma classeEmployee, que implementaUserTrait:
class Employee implements UserTrait {}
Agora, vamos criar um teste para verificar se uma instânciaEmployee pode acessar o métodothe sayHello deUserTrait:
def 'Should return msg string when using Employee.sayHello method provided by User trait' () {
when:
def msg = employee.sayHello()
then:
msg
msg instanceof String
assert msg == "Hello!"
}
3.2. Métodos particulares
Também podemos criar um métodoprivate em umtraite nos referir a ele em outro métodopublic.
Vamos ver a implementação do código emUserTrait:
private String greetingMessage() {
return 'Hello, from a private method!'
}
String greet() {
def msg = greetingMessage()
println msg
return msg
}
Observe que se acessarmos o métodoprivate na classe de implementação, ele lançará umMissingMethodException:
def 'Should return MissingMethodException when using Employee.greetingMessage method' () {
when:
def exception
try {
employee.greetingMessage()
} catch(Exception e) {
exception = e
}
then:
exception
exception instanceof groovy.lang.MissingMethodException
assert exception.message == "No signature of method: com.example.traits.Employee.greetingMessage()"
+ " is applicable for argument types: () values: []"
}
Em atrait,, um método privado pode ser essencial para qualquer implementação que não deva ser substituída por nenhuma classe, embora seja exigido por outros métodos públicos.
3.3. Métodos abstratos
Umtrait também pode conter métodosabstract que podem ser implementados em outra classe:
trait UserTrait {
abstract String name()
String showName() {
return "Hello, ${name()}!"
}
}
class Employee implements UserTrait {
String name() {
return 'Bob'
}
}
3.4. Substituindo Métodos Padrão
Normalmente, atrait contém implementações padrão de seus métodos públicos, mas podemos substituí-los na classe de implementação:
trait SpeakingTrait {
String speak() {
return "Speaking!!"
}
}
class Dog implements SpeakingTrait {
String speak() {
return "Bow Bow!!"
}
}
As características não suportam escoposprotectedeprivate.
4. this palavra-chave
O comportamento da palavra-chave this é semelhante ao de Java. Podemos considerartrait como uma classesuper.
Por exemplo, vamos criar um método que retornathis emtrait:
trait UserTrait {
def self() {
return this
}
}
5. Interfaces
Atrait também pode implementar interfaces, assim como as classes regulares fazem.
Vamos criar uminterfacee implementá-lo em umtrait:
interface Human {
String lastName()
}
trait UserTrait implements Human {
String showLastName() {
return "Hello, ${lastName()}!"
}
}
Agora, vamos implementar o métodoabstract deinterface na classe de implementação:
class Employee implements UserTrait {
String lastName() {
return "Marley"
}
}
6. Propriedades
Podemos adicionar propriedades atrait assim como faríamos em qualquer classe regular:
trait UserTrait implements Human {
String email
String address
}
7. Extendendo traços
Semelhante a umclass Groovy regular, umtrait pode estender outrotrait usando a palavra-chaveextends:
trait WheelTrait {
int noOfWheels
}
trait VehicleTrait extends WheelTrait {
String showWheels() {
return "Num of Wheels $noOfWheels"
}
}
class Car implements VehicleTrait {}
Também podemos estender vários traços com a cláusulaimplements:
trait AddressTrait {
String residentialAddress
}
trait EmailTrait {
String email
}
trait Person implements AddressTrait, EmailTrait {}
8. Conflitos múltiplos de herança
Quando uma classe implementa duas ou mais características que possuem métodos com a mesma assinatura, precisamos saber como resolver os conflitos. Vejamos como o Groovy resolve esses conflitos por padrão, bem como uma maneira de substituir a resolução padrão.
8.1. Resolução padrão de conflitos
By default, the method from the last declared trait in the implements clause will be picked up.
Portanto, as características nos ajudam a implementar várias heranças sem encontrar oDiamond Problem.
Primeiro, vamos criar duas características com um método com a mesma assinatura:
trait WalkingTrait {
String basicAbility() {
return "Walking!!"
}
}
trait SpeakingTrait {
String basicAbility() {
return "Speaking!!"
}
}
A seguir, vamos escrever uma classe que implemente ambas as características:
class Dog implements WalkingTrait, SpeakingTrait {}
ComoSpeakingTrait é declarado por último, sua implementação de métodobasicAbility seria escolhida por padrão na classeDog.
8.2. Resolução explícita de conflitos
Agora, se não quisermos simplesmente pegar a resolução de conflito padrão fornecida pela linguagem, podemos substituí-labyexplicitly choosing which method to call using the trait.super.method reference.
Por exemplo, vamos adicionar outro método com a mesma assinatura às nossas duas características:
String speakAndWalk() {
return "Walk and speak!!"
}
String speakAndWalk() {
return "Speak and walk!!"
}
Agora, vamos substituir a resolução padrão de conflitos de herança múltipla em nossa classeDog usando a palavra-chavesuper:
class Dog implements WalkingTrait, SpeakingTrait {
String speakAndWalk() {
WalkingTrait.super.speakAndWalk()
}
}
9. Implementando traços em tempo de execução
Para implementar atrait dinamicamente, podemosuse the as keyword to coerce an object to a trait at runtime.
Por exemplo, vamos criar umAnimalTrait com o métodobasicBehavior:
trait AnimalTrait {
String basicBehavior() {
return "Animalistic!!"
}
}
Para implementar várias características de uma vez, podemos usar o métodowithTraits em vez da palavra-chaveas:
def dog = new Dog()
def dogWithTrait = dog.withTraits SpeakingTrait, WalkingTrait, AnimalTrait
10. Conclusão
Neste artigo, vimos como criar características no Groovy e exploramos alguns de seus recursos úteis.
Atrait é uma maneira realmente eficaz de adicionar implementações e funcionalidades comuns em todas as nossas classes. Além disso, ele permite minimizar o código redundante e facilita a manutenção do código.
Como de costume, as implementações de código e os testes de unidade para este artigo estão disponíveisover on GitHub.