É uma prática segura usar métodos padrão como uma versão pobre de características no Java 8?
Alguns afirmam que pode deixar os pandas tristes se você usá-los apenas pelo uso, porque é legal, mas essa não é minha intenção. Também é frequentemente lembrado que os métodos padrão foram introduzidos para oferecer suporte à evolução da API e à compatibilidade com versões anteriores, o que é verdade, mas isso não torna errado ou torcido usá-los como características per se.
Tenho o seguinte caso de uso prático em mente:
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
Ou talvez, defina um PeriodTrait
:
public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}
É certo que a composição poderia ser usada (ou mesmo classes auxiliares), mas parece mais prolixo e confuso e não permite o benefício do polimorfismo.
Portanto, é ok / seguro usar métodos padrão como características básicas ou devo me preocupar com efeitos colaterais imprevistos?
Várias perguntas sobre SO estão relacionadas a características Java vs Scala; esse não é o ponto aqui. Não estou pedindo apenas opiniões. Em vez disso, estou procurando uma resposta confiável ou pelo menos uma visão de campo: se você usou métodos padrão como características em seu projeto corporativo, acabou sendo uma bomba-relógio?
fonte
Respostas:
A resposta curta é: é seguro se você usá-los com segurança :)
A resposta sarcástica: diga-me o que você entende por traços, e talvez eu lhe dê uma resposta melhor :)
Com toda a seriedade, o termo "traço" não é bem definido. Muitos desenvolvedores Java estão mais familiarizados com as características conforme são expressas no Scala, mas o Scala está longe de ser a primeira linguagem a ter características, seja no nome ou em efeito.
Por exemplo, em Scala, os traits são stateful (podem ter
var
variáveis); em Fortaleza, são comportamento puro. As interfaces do Java com métodos padrão não têm estado; isso significa que eles não são traços? (Dica: essa era uma pergunta capciosa.)Novamente, em Scala, os traços são compostos por linearização; se a classe
A
estende os traçosX
eY
, então a ordem em queX
eY
são misturados determina como os conflitos entreX
eY
são resolvidos. Em Java, esse mecanismo de linearização não está presente (foi rejeitado, em parte, porque era muito "não semelhante ao Java".)O motivo aproximado para adicionar métodos padrão às interfaces era para suportar a evolução da interface , mas estávamos bem cientes de que estávamos indo além disso. Se você considera isso uma "evolução da interface ++" ou "características -" é uma questão de interpretação pessoal. Então, para responder à sua pergunta sobre segurança ... contanto que você se atenha ao que o mecanismo realmente suporta, ao invés de tentar esticá-lo para algo que ele não suporta, você deve ficar bem.
Um dos principais objetivos do projeto era que, da perspectiva do cliente de uma interface, os métodos padrão fossem indistinguíveis dos métodos de interface "regulares". O default de um método, portanto, só é interessante para o designer e implementador da interface.
Aqui estão alguns casos de uso que estão bem dentro dos objetivos do design:
Evolução da interface. Aqui, estamos adicionando um novo método a uma interface existente, que tem uma implementação padrão sensata em termos de métodos existentes nessa interface. Um exemplo seria adicionar o
forEach
método aCollection
, onde a implementação padrão é escrita em termos doiterator()
método.Métodos "opcionais". Aqui, o designer de uma interface está dizendo "Os implementadores não precisam implementar este método se estiverem dispostos a conviver com as limitações de funcionalidade que isso acarreta". Por exemplo,
Iterator.remove
foi dado um padrão que lançaUnsupportedOperationException
; como a grande maioria das implementações deIterator
possui esse comportamento, o padrão torna esse método essencialmente opcional. (Se o comportamento deAbstractCollection
foi expresso como padrãoCollection
, podemos fazer o mesmo para os métodos mutativos.)Métodos de conveniência. Esses são métodos estritamente por conveniência, novamente geralmente implementados em termos de métodos não padrão na classe. O
logger()
método em seu primeiro exemplo é uma ilustração razoável disso.Combinadores. Esses são métodos de composição que instanciam novas instâncias da interface com base na instância atual. Por exemplo, os métodos
Predicate.and()
ouComparator.thenComparing()
são exemplos de combinadores.Se você fornecer uma implementação padrão, também deverá fornecer algumas especificações para o padrão (no JDK, usamos a
@implSpec
tag javadoc para isso) para ajudar os implementadores a entender se desejam substituir o método ou não. Alguns padrões, como métodos de conveniência e combinadores, quase nunca são substituídos; outros, como métodos opcionais, são freqüentemente substituídos. Você precisa fornecer especificações suficientes (não apenas documentação) sobre o que o padrão promete fazer, para que o implementador possa tomar uma decisão sensata sobre se eles precisam substituí-la.fonte
MouseListener
? Na medida em que esse estilo de API faz sentido, ele se encaixa no bloco "métodos opcionais". Certifique-se de documentar claramente o ness opcional!MouseListener
. Obrigado pela resposta.