Uma das novidades da versão 9 do Java são os métodos com
visibilidade privada nas interfaces. Este novo elemento, acrescido aos métodos
default e estáticos incluídos na versão 8, vem para completar as alternativas construtivas
nas interfaces.
Interfaces
Uma interface é a definição de conjunto de elementos
visíveis, ou seja, públicos, dos objetos de uma classe. Quando a implementação
de uma classe contém todas as operações de conjunto particular, dizemos que tal
classe realiza tal interface, ou seja, esta classe atende as especificações
ditadas pela interface implementada.
Uma interface Java é algo simples como:
package
jandl.j9;
public
interface Adder {
void add(Number
value);
Number getTotal();
void
setTotal(Number initValue);
void
reset();
}
Implicitamente, todas as operações de uma interface são, por
padrão, públicas e abstratas, ou seja, todas poderiam receber o especificador
de acesso public e o modificador abstract em suas declarações.
Desta maneira, uma interface é como um contrato, que dita
quais operações devem estar disponíveis para obter-se um conjunto específico de
funcionalidade (e de interoperabilidade, portanto). Isto é particularmente
importante, pois a questão aqui é como os objetos podem ser utilizados, e não
como foram implementados.
A classe AdderImpl que segue é uma implementação possível da
interface Adder.
package
jandl.j9;
public
class AdderImpl implements Adder {
private
double total;
public
AdderImpl() { reset(); }
public
AdderImpl(Number value) { setTotal(value); }
@Override
public void
add(Number value) { total = total + ((Number) value).doubleValue(); }
@Override
public Number
getTotal() { return new Double(total); }
@Override
public void
setTotal(Number initValue) { total = ((Number) initValue).doubleValue(); }
@Override
public void
reset() { total = 0; }
}
Desta maneira, classes pertencentes a diferentes hierarquias
de objetos, mas que implementam uma interface comum, podem, polimorficamente,
serem tratadas como de um mesmo tipo que corresponde a tal interface.
A modelagem de sistemas por meio da definição de interfaces
é uma prática reconhecidamente boa, também conhecida como programação por
contratos.
Assim, o uso de interfaces é particularmente importante no
projeto de sistemas, o que também é enfatizado pelos princípios SOLID
(discutidos numa série de posts iniciados por Os Princípios SOLID – Parte I).
Evolução de interfaces
Apesar das muitas conveniências no projeto e uso de
interfaces, surgem situações onde uma interface necessita ser alterada. Aí o
cenário deixa de positivo. Modificar uma interface, no sentido de alterar a
assinatura de um dos métodos presentes ou acrescentar novas operações cria a
exigência de modificar ou completar todas as classes que realizam tal
interface, numa constrangedora propagação das alterações realizadas.
A partir do Java 8 se tornou possível realizar alterações
numa interface existente, mantendo a compatibilidade com suas versões
anteriores, isto é, sem propagar as alterações, simplificando muito o trabalho
de manutenção do código. Para isto foram introduzidos os métodos default e
estáticos às interfaces.
Os métodos default são aqueles declarados com o
especificador de visibilidade public
e com o novo modificador default.
Além disso, tais métodos devem ser implementados na própria interface, pois não
são abstratos, o que evita a propagação da modificação.
A interface Adder poderia receber um novo método, como
segue, sem que as classes que a realizam sejam afetadas.
package
jandl.j9;
public
interface Adder {
:
// método default, cuja implementação é adicionada na
interface modificada
default
void add(Adder adder) { add(adder.getTotal());
}
}
De forma análoga, os métodos estáticos são aqueles
declarados com o especificador de visibilidade public e com o conhecido modificador static, o que também exige sua própria interface, pois, como
qualquer elemento estático, pertencem a seu tipo e não a suas instâncias. Da
mesma forma sua adição não propaga qualquer efeito nas classes que já realizam
a interface modificada.
package
jandl.j9;
public
interface Adder {
:
// método estático, cuja implementação é adicionada na
interface modificada
static void
add(Adder adder, List<Number> list) {
list.stream().forEach((value)->{
adder.add(value); });
}
}
As duas alternativas, dos métodos default e dos métodos
estáticos, possibilitam a evolução de interfaces existentes, sem propagação de
alterações e com garantia da compatibilidade binária com suas versões antigas.
Métodos privados em interfaces
A adição da possibilidade de declarar métodos privados em
interfaces, embora estranha à primeira vista, é um complemento para auxiliar o
programador no uso dos métodos default e métodos estáticos nas interfaces.
Os novos métodos privados são
declarados com o especificador de visibilidade private e, opcionalmente, com o
modificar static. Como qualquer membro privado, só podem ser acessados pelos
demais membros do seu tipo.
Sendo assim, o uso
de métodos privados, estáticos ou não, numa interface tem como objetivo
permitir que o programador defina operações auxiliares para os demais métodos
default e estáticos públicos, sem expor tal operação.
Na nova implementação da interface Adder, um método privado
estático add(Adder, Number[]) provê o serviço de adição para três outros métodos
da interface.
package
jandl.j9;
import
java.util.List;
public
interface Adder {
void
add(Number value);
Number
getTotal();
void
setTotal(Number initValue);
void
reset();
default
void add(Adder adder) { add(adder.getTotal()); }
// métodos default e estáticos que utilizam método privado
desta interface
default
void add(Number[] array) { add(this, array); }
default
void add(List<Number> list) { add(this, list.toArray(new Number[0])); }
static void
add(Adder adder, List<Number> list) {
add(adder, list.toArray(new
Number[0]));
}
// método estático privado que prove serviço para os demais
private static
void add(Adder adder, Number[] array) {
for(int i=0;
i<array.length;i++) { adder.add(array[i]);
}
}
}
Considerações Finais
As interfaces na versão 9 Java suportam, então, uma variada
combinação de especificadores e modificadores, como sumarizado na tabela que
segue:
Especificador
|
Modificador
|
Situação
|
Nenhum
|
Nenhum
|
Declaração válida de método, implicitamente, público e abstrato.
|
Nenhum
|
abstract
|
Declaração válida de método abstrato, implicitamente público.
|
Nenhum
|
default
|
Declaração válida de método default, implicitamente público.
|
Nenhum
|
static
|
Declaração válida de método estático, implicitamente público.
|
public
|
Nenhum
|
Declaração válida de método público, implicitamente abstrato.
|
public
|
abstract
|
Declaração válida explícita de método público e abstrato.
|
public
|
default
|
Declaração válida explícita de método público e default.
|
public
|
static
|
Declaração válida explícita de método público e estático.
|
private
|
Nenhum
|
Declaração válida explícita de método privado.
|
private
|
abstract
|
Declaração inválida. Erro de compilação.
|
private
|
default
|
Declaração inválida. Erro de compilação.
|
private
|
static
|
Declaração válida explícita de método privado e estático.
|
Os métodos privados não são herdados por subinterfaces ou
implementações das classes.
Finalmente, apesar da utilidade exemplificada, de permitir
modificações em interfaces existentes sem a propagação de efeitos colaterais
indesejados, cabe destacar que, se uma interface existente deve ser modificada,
isto indica duas situações: alterações nas regras de negócio da aplicação ou
identificação de falhas no projeto do software. Enquanto a primeira razão pode
ser imprevisível e inevitável; a segunda reforça o cuidado necessário com o
projeto de qualquer software.
A implementação de métodos (default ou estáticos, públicos
ou privados) é, do ponto de vista da Orientação a Objetos, uma violação dos
seus propósitos. Se é necessária uma implementação parcial de uma interface, é
mais adequada a construção de uma classe abstrata contendo a codificação destas
operações, mantendo a interface como uma classe abstrata pura (sem
implementação de qualquer operação).
Reforço que isto não é um defeito do Java, mas, acredito,
alternativas possíveis na plataforma para solução de problemas envolvendo
interfaces.
Assim, muito cuidado no projeto de suas interfaces, que
devem conter apenas as operações minimamente necessárias (tal como defendido pelo
princípio de segregação das interfaces ou ISP dos princípios SOLID). Qualquer
coisa diferente disso, pode não ser adequada, mesmo com todas as possibilidades
ofertadas pela plataforma Java.
Para saber mais
- http://blog.joda.org/2016/09/private-methods-in-interfaces-in-java-9.html
- https://www.journaldev.com/12850/java-9-private-methods-interfaces
- https://www.journaldev.com/13121/java-9-features-with-examples
Nenhum comentário:
Postar um comentário