Métodos estáticos e padrão em interfaces em Java
1. Visão geral
Java 8 trouxe para a mesa alguns novos recursos, incluindolambda expressions,functional interfaces,method references,streams,Optional estatic e Métodosdefault em interfaces.
Alguns deles já foram cobertos emthis article. No entanto, os métodosstaticedefault em interfaces merecem uma análise mais aprofundada por conta própria.
Neste artigo, discutiremos em profundidadehow to use static and default methods in interfacese analisaremos alguns casos de uso em que podem ser úteis.
2. Por que os métodos padrão em interfaces são necessários
Como métodos de interface regulares,default methods are implicitly public - não há necessidade de especificar o modificadorpublic.
Ao contrário dos métodos de interface regulares, eles sãodeclared with the default keyword at the beginning of the method signature eprovide an implementation.
Vejamos um exemplo simples:
public interface MyInterface {
// regular interface methods
default void defaultMethod() {
// default method implementation
}
}
A razão pela qual os métodosdefault foram incluídos no lançamento do Java 8 é bastante óbvia.
Em um design típico baseado em abstrações, em que uma interface possui uma ou várias implementações, se um ou mais métodos forem adicionados à interface, todas as implementações também serão forçadas a implementá-las. Caso contrário, o design será quebrado.
Os métodos de interface padrão são uma maneira eficiente de lidar com esse problema. Elesallow us to add new methods to an interface that are automatically available in the implementations. Assim, não há necessidade de modificar as classes de implementação.
Desta forma,backward compatibility is neatly preserved sem ter que refatorar os implementadores.
3. Métodos de interface padrão em ação
Para entender melhor a funcionalidade dos métodos de interface dedefault, vamos criar um exemplo simples.
Digamos que temos uma interfaceVehicle ingênua e apenas uma implementação. Pode haver mais, mas vamos mantê-lo simples:
public interface Vehicle {
String getBrand();
String speedUp();
String slowDown();
default String turnAlarmOn() {
return "Turning the vehicle alarm on.";
}
default String turnAlarmOff() {
return "Turning the vehicle alarm off.";
}
}
E vamos escrever a classe de implementação:
public class Car implements Vehicle {
private String brand;
// constructors/getters
@Override
public String getBrand() {
return brand;
}
@Override
public String speedUp() {
return "The car is speeding up.";
}
@Override
public String slowDown() {
return "The car is slowing down.";
}
}
Por último, vamos definir uma classemain típica, que cria uma instância deCare chama seus métodos:
public static void main(String[] args) {
Vehicle car = new Car("BMW");
System.out.println(car.getBrand());
System.out.println(car.speedUp());
System.out.println(car.slowDown());
System.out.println(car.turnAlarmOn());
System.out.println(car.turnAlarmOff());
}
Observe como os métodosdefaultturnAlarmOn()eturnAlarmOff() de nossa interfaceVehicle sãoautomatically available in the Car class.
Além disso, se em algum ponto decidirmos adicionar mais métodosdefault à interfaceVehicle, o aplicativo continuará funcionando e não teremos que forçar a classe a fornecer implementações para os novos métodos .
O uso mais comum de métodos padrão em interfaces éto incrementally provide additional functionality to a given type without breaking down the implementing classes.
Além disso, eles podem ser usados paraprovide additional functionality around an existing abstract method:
public interface Vehicle {
// additional interface methods
double getSpeed();
default double getSpeedInKMH(double speed) {
// conversion
}
}
4. Regras de herança de interface múltipla
Os métodos de interface padrão são um recurso bastante interessante, mas com algumas ressalvas que vale a pena mencionar. Como o Java permite que as classes implementem várias interfaces, é importante saberwhat happens when a class implements several interfaces that define the same default methods.
Para entender melhor este cenário, vamos definir uma nova interfaceAlarm e refatorar a classeCar:
public interface Alarm {
default String turnAlarmOn() {
return "Turning the alarm on.";
}
default String turnAlarmOff() {
return "Turning the alarm off.";
}
}
Com esta nova interface definindo seu próprio conjunto de métodosdefault, a classeCar implementariaVehicleeAlarm:
public class Car implements Vehicle, Alarm {
// ...
}
Nesse caso,the code simply won’t compile, as there’s a conflict caused by multiple interface inheritance (a.k.a oDiamond Problem). A classeCar herdaria ambos os conjuntos de métodosdefault. Quais devem ser chamados então?
Para resolver essa ambigüidade, devemos fornecer explicitamente uma implementação para os métodos:
@Override
public String turnAlarmOn() {
// custom implementation
}
@Override
public String turnAlarmOff() {
// custom implementation
}
Também podemoshave our class use the default methods of one of the interfaces.
Vejamos um exemplo que usa os métodosdefault da interfaceVehicle:
@Override
public String turnAlarmOn() {
return Vehicle.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Vehicle.super.turnAlarmOff();
}
Da mesma forma, podemos fazer com que a classe use os métodosdefault definidos na interfaceAlarm:
@Override
public String turnAlarmOn() {
return Alarm.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Alarm.super.turnAlarmOff();
}
Além disso, é mesmopossible to make the Car class use both sets of default methods:
@Override
public String turnAlarmOn() {
return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}
5. Métodos de interface estática
Além de poder declarar métodosdefault nas interfaces,Java 8 allows us to define and implement static methods in interfaces.
Como os métodosstatic não pertencem a um objeto específico, eles não fazem parte da API das classes que implementam a interface e devem sercalled by using the interface name preceding the method name.
Para entender como os métodosstatic funcionam nas interfaces, vamos refatorar a interfaceVehicle e adicionar a ela um método utilitáriostatic:
public interface Vehicle {
// regular / default interface methods
static int getHorsePower(int rpm, int torque) {
return (rpm * torque) / 5252;
}
}
Defining a static method within an interface is identical to defining one in a class. Além disso, um métodostatic pode ser chamado em outros métodosstaticedefault.
Agora, digamos que queremos calcular ohorsepower do motor de um determinado veículo. Nós apenas chamamos o métodogetHorsePower():
Vehicle.getHorsePower(2500, 480));
A ideia por trás dos métodos de interfacestatic é fornecer um mecanismo simples que nos permiteincrease the degree of cohesion de um projeto, juntando métodos relacionados em um único lugar, sem ter que criar um objeto.
Praticamentethe same can be done with abstract classes. A principal diferença reside no fato de queabstract classes can have constructors, state, and behavior.
Além disso, métodos estáticos nas interfaces possibilitam agrupar métodos utilitários relacionados, sem ter que criar classes de utilitários artificiais que são simplesmente espaços reservados para métodos estáticos.
6. Conclusão
Neste artigo, exploramos em profundidade o uso dos métodos de interfacestaticedefault em Java 8. À primeira vista, esse recurso pode parecer um pouco desleixado, principalmente de uma perspectiva purista orientada a objetos. Idealmente, as interfaces não devem encapsular o comportamento e devem ser usadas apenas para definir a API pública de um determinado tipo.
Quando se trata de manter a compatibilidade com versões anteriores com o código existente, no entanto, os métodosstaticedefault são uma boa opção.
E, como de costume, todos os exemplos de código mostrados neste artigo estão disponíveisover on GitHub.