Um dos recursos mais úteis do Java 8 são os novos default
métodos nas interfaces. Existem essencialmente duas razões (podem existir outras) pelas quais elas foram introduzidas:
- Fornecendo implementações padrão reais. Exemplo:
Iterator.remove()
- Permitindo a evolução da API do JDK. Exemplo:
Iterable.forEach()
Do ponto de vista de um designer de API, eu gostaria de poder usar outros modificadores nos métodos de interface, por exemplo final
. Isso seria útil ao adicionar métodos de conveniência, evitando substituições "acidentais" na implementação de classes:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
O acima já é prática comum se Sender
fosse uma classe:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Agora, default
e final
obviamente estão contradizendo palavras-chave, mas a palavra-chave padrão em si não teria sido estritamente necessária , portanto, suponho que essa contradição seja deliberada, para refletir as diferenças sutis entre "métodos de classe com corpo" (apenas métodos) e "interface métodos com corpo " (métodos padrão), ou seja, diferenças que ainda não compreendi.
Em algum ponto do tempo, o suporte para modificadores como static
e final
sobre métodos de interface ainda não foi totalmente explorado, citando Brian Goetz :
A outra parte é até que ponto vamos dar suporte a ferramentas de criação de classe em interfaces, como métodos finais, métodos privados, métodos protegidos, métodos estáticos etc. A resposta é: ainda não sabemos
Desde então, no final de 2011, obviamente, static
foi adicionado suporte a métodos em interfaces. Claramente, isso agregou muito valor às próprias bibliotecas JDK, como com Comparator.comparing()
.
Questão:
Qual é o motivo final
(e também static final
) nunca chegou às interfaces Java 8?
fonte
final
impede que um método seja substituído e, vendo como DEVE substituir métodos herdados de interfaces, não vejo por que faria sentido torná-lo final. A menos que isso signifique que o método é final APÓS substituí-lo uma vez .. Nesse caso, talvez haja q? Se não estou entendendo direito, por favor, deixe-me entender. Parece interessantefinal
seria útil impedir que as classes de implementação substituíssem a implementação padrão de um método de interface.Respostas:
Esta questão está, em algum grau, relacionada a Qual é o motivo pelo qual “sincronizado” não é permitido nos métodos de interface Java 8?
O principal a entender sobre métodos padrão é que o objetivo principal do projeto é a evolução da interface , não "transformar interfaces em características (medíocres)". Embora exista alguma sobreposição entre os dois, e tentamos nos acomodar ao último onde não atrapalhou o primeiro, essas perguntas são melhor entendidas quando vistas sob essa luz. (Nota também que métodos de classe são vai ser diferente a partir de métodos de interface, não importa o que a intenção, em virtude do fato de que os métodos de interface pode ser multiplamente herdada.)
A idéia básica de um método padrão é: é um método de interface com uma implementação padrão e uma classe derivada pode fornecer uma implementação mais específica. E como o centro de design era a evolução da interface, era um objetivo crítico do projeto que os métodos padrão pudessem ser adicionados às interfaces após o fato de maneira compatível com a origem e compatível com o binário.
A resposta muito simples para "por que não os métodos padrão finais" é que então o corpo não seria simplesmente a implementação padrão, seria a única implementação. Embora seja uma resposta um pouco simples demais, ela nos dá uma pista de que a pergunta já está indo em uma direção questionável.
Outra razão pela qual os métodos finais de interface são questionáveis é que eles criam problemas impossíveis para os implementadores. Por exemplo, suponha que você tenha:
Aqui tudo está bem;
C
herdafoo()
deA
. Agora, supondo queB
seja alterado para ter umfoo
método, com um padrão:Agora, quando formos recompilar
C
, o compilador nos dirá que não sabe para qual comportamento herdarfoo()
; portantoC
, deve substituí-lo (e pode optar por delegarA.super.foo()
se quiser manter o mesmo comportamento.) Mas e seB
tinha feito o seu padrãofinal
, eA
não está sob o controle do autor deC
? AgoraC
está irremediavelmente quebrado; não pode compilar sem substituirfoo()
, mas não pode substituirfoo()
se foi finalB
.Este é apenas um exemplo, mas o ponto é que a finalidade dos métodos é realmente uma ferramenta que faz mais sentido no mundo das classes de herança única (geralmente que associam o estado ao comportamento) do que às interfaces que apenas contribuem com o comportamento e podem ser multiplicadas. herdado. É muito difícil argumentar sobre "que outras interfaces podem ser misturadas no eventual implementador", e permitir que um método de interface seja final provavelmente causaria esses problemas (e eles não explodiriam na pessoa que escreveu a interface, mas no usuário pobre que tenta implementá-lo.)
Outro motivo para proibi-los é que eles não significam o que você pensa que eles significam. Uma implementação padrão é considerada apenas se a classe (ou suas superclasses) não fornecer uma declaração (concreta ou abstrata) do método. Se um método padrão fosse final, mas uma superclasse já o implementasse, o padrão seria ignorado, o que provavelmente não é o que o autor padrão esperava ao declará-lo final. (Esse comportamento de herança é um reflexo do centro de design para métodos padrão - evolução da interface. Deve ser possível adicionar um método padrão (ou uma implementação padrão a um método de interface existente) a interfaces existentes que já possuem implementações, sem alterar o comportamento de classes existentes que implementam a interface,
fonte
Na lista de correio lambda, há muitas discussões sobre isso . Um dos que parece conter muita discussão sobre tudo isso é o seguinte: Visibilidade do método de interface variada (eram os defensores finais) .
Nesta discussão, Talden, o autor da pergunta original, pergunta algo muito semelhante à sua pergunta:
Eventualmente, a resposta de Brian Goetz foi:
Portanto, provavelmente nunca foi implementado porque nunca fez parte do escopo. Nunca foi proposto a tempo de ser considerado.
Em outra discussão acalorada sobre os métodos de defensor final sobre o assunto, Brian disse novamente :
Portanto, isso reforça minha teoria de que simplesmente não fazia parte do escopo ou de seu design. O que eles fizeram foi fornecer funcionalidade suficiente para lidar com os problemas da evolução da API.
fonte
Vai ser difícil de encontrar e identificar "a" resposta, para os resons mencionados nos comentários de @EJP: Há cerca de 2 (+/- 2) pessoas no mundo que podem dar a resposta definitiva em tudo . E na dúvida, a resposta pode ser algo como "O suporte aos métodos finais padrão não parecia valer o esforço de reestruturar os mecanismos internos de resolução de chamadas". Isso é especulação, é claro, mas é pelo menos apoiado por evidências sutis, como esta declaração (por uma das duas pessoas) na lista de discussão do OpenJDK :
e fatos triviais como esse simplesmente não são considerados um método (realmente) final quando é um
default
método, conforme implementado atualmente no método Method :: is_final_method no OpenJDK.É realmente difícil encontrar informações realmente "autoritárias", mesmo com pesquisas na web excessivas e lendo logs de confirmação. Eu pensei que isso poderia estar relacionado a possíveis ambiguidades durante a resolução de chamadas de método de interface com as
invokeinterface
instruções e chamadas de método de classe, correspondentes àinvokevirtual
instrução: Para ainvokevirtual
instrução, pode haver uma pesquisa simples da vtable , porque o método deve ser herdado de uma superclasse ou implementada diretamente pela classe. Em contraste com isso, umainvokeinterface
chamada deve examinar o respectivo site de chamada para descobrir a qual interface essa chamada realmente se refere (isso é explicado em mais detalhes na página InterfaceCalls do Wiki do HotSpot). Contudo,final
os métodos não são inseridos na vtable de maneira alguma ou substituem as entradas existentes na vtable (consulte klassVtable.cpp. Linha 333 ) e, da mesma forma, os métodos padrão estão substituindo as entradas existentes na vtable (consulte klassVtable.cpp, linha 202 ) . Portanto, o motivo real (e, portanto, a resposta) deve ser oculto mais profundamente nos mecanismos (bastante complexos) de resolução de chamadas do método, mas talvez essas referências sejam consideradas úteis, seja apenas para outras pessoas que conseguem derivar a resposta real a partir desse.fonte
int
s, ainda ver stackoverflow.com/questions/430346 ... ...synchronized
aosdefault
métodos.final
pergunta, e é um tanto humilhante que ninguém mais parecesse pensar em exemplos semelhantes (ou, talvez, alguns pensassem nesses exemplos, mas não parecessem suficientemente autoritários para responder). Então agora a palavra final (desculpe, eu tenho que fazer isso :) é falada.Eu não acho que seja necessário especificar
final
um método de interface de conveniência, mas posso concordar que isso pode ser útil, mas aparentemente os custos superam os benefícios.O que você deve fazer, de qualquer maneira, é escrever o javadoc apropriado para o método padrão, mostrando exatamente o que o método é e não tem permissão para fazer. Dessa forma, as classes que implementam a interface "não têm permissão" para alterar a implementação, embora não haja garantias.
Qualquer um poderia escrever um
Collection
que aderisse à interface e, em seguida, faça coisas nos métodos absolutamente contrários à intuição; não há como se proteger disso, além de escrever extensos testes de unidade.fonte
final
foi decidido não ser permitido nosinterface
métodos Java 8 . A relação custo / benefício insuficiente é um bom candidato, mas até agora isso é especulação.Adicionamos
default
palavras-chave ao nosso método dentro de uminterface
quando sabemos que a classe que estende a implementaçãointerface
pode ou não seroverride
implementada. Mas e se quisermos adicionar um método que não queremos que nenhuma classe de implementação substitua? Bem, duas opções estavam disponíveis para nós:default
final
métodostatic
métodoAgora, Java diz que, se tivermos uma
class
implementação de duas ou mais, deinterfaces
modo que eles tenham umdefault
método com exatamente o mesmo nome e assinatura de método, ou seja, duplicados, precisamos fornecer uma implementação desse método em nossa classe. Agora, no caso dedefault
final
métodos, não podemos fornecer uma implementação e estamos presos. E é por isso que afinal
palavra-chave não é usada nas interfaces.fonte