Java 8 permite métodos de interface estática
Com o Java 8, as interfaces podem ter métodos estáticos. Eles também podem ter métodos de instância concretos, mas não campos de instância.
Há realmente duas perguntas aqui:
- Por que, nos velhos tempos, as interfaces não podiam conter métodos estáticos?
- Por que métodos estáticos não podem ser substituídos?
Métodos estáticos em interfaces
Não havia uma forte razão técnica pela qual as interfaces não poderiam ter métodos estáticos nas versões anteriores. Isso é resumido muito bem pelo pôster de uma pergunta duplicada. Os métodos de interface estática foram inicialmente considerados como uma pequena alteração de linguagem e, em seguida, houve uma proposta oficial para adicioná-los no Java 7, mas posteriormente foi descartada devido a complicações imprevistas.
Por fim, o Java 8 introduziu métodos de interface estática, bem como métodos de instância substituíveis por uma implementação padrão. Eles ainda não podem ter campos de instância. Esses recursos fazem parte do suporte à expressão lambda e você pode ler mais sobre eles na Parte H do JSR 335.
Substituindo métodos estáticos
A resposta para a segunda pergunta é um pouco mais complicada.
Métodos estáticos são resolvíveis em tempo de compilação. O despacho dinâmico faz sentido, por exemplo, nos métodos em que o compilador não pode determinar o tipo concreto do objeto e, portanto, não pode resolver o método a ser chamado. Mas invocar um método estático requer uma classe e, como essa classe é conhecida estaticamente - em tempo de compilação -, o envio dinâmico é desnecessário.
Um pequeno histórico de como os métodos de instância funcionam é necessário para entender o que está acontecendo aqui. Tenho certeza de que a implementação real é bem diferente, mas deixe-me explicar minha noção de envio de método, que modela o comportamento com precisão.
Finja que cada classe tem uma tabela de hash que mapeia assinaturas de métodos (tipos de nomes e parâmetros) para um pedaço de código real para implementar o método. Quando a máquina virtual tenta chamar um método em uma instância, consulta o objeto para sua classe e procura a assinatura solicitada na tabela da classe. Se um corpo de método for encontrado, ele será chamado. Caso contrário, a classe pai da classe é obtida e a pesquisa é repetida lá. Isso continua até que o método seja encontrado ou que não haja mais classes pai - o que resulta em a NoSuchMethodError
.
Se uma superclasse e uma subclasse possuem uma entrada em suas tabelas para a mesma assinatura de método, a versão da subclasse é encontrada primeiro e a versão da superclasse nunca é usada - isso é uma "substituição".
Agora, suponha que pularemos a instância do objeto e apenas começaremos com uma subclasse. A resolução pode prosseguir como acima, fornecendo uma espécie de método estático "substituível". No entanto, a resolução pode ocorrer em tempo de compilação, pois o compilador está iniciando a partir de uma classe conhecida, em vez de aguardar até o tempo de execução para consultar um objeto de um tipo não especificado para sua classe. Não há sentido em "substituir" um método estático, pois sempre é possível especificar a classe que contém a versão desejada.
Construtor "interfaces"
Aqui está um pouco mais de material para abordar a edição recente da pergunta.
Parece que você deseja efetivamente exigir um método semelhante ao construtor para cada implementação do IXMLizable
. Esqueça de tentar impor isso com uma interface por um minuto e finja que você tem algumas classes que atendem a esse requisito. Como você usaria isso?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Como você precisa nomear explicitamente o tipo concreto Foo
ao "construir" o novo objeto, o compilador pode verificar se ele realmente possui o método de fábrica necessário. E se não, e daí? Se eu puder implementar um IXMLizable
que não tenha o "construtor" e criar uma instância e passá-la ao seu código, será uma IXMLizable
com toda a interface necessária.
A construção faz parte da implementação, não a interface. Qualquer código que funcione com sucesso com a interface não se importa com o construtor. Qualquer código que se preocupe com o construtor precisa conhecer o tipo concreto de qualquer maneira, e a interface pode ser ignorada.