Por que uma interface aninhada estática seria usada em Java?

235

Acabei de encontrar uma interface aninhada estática em nossa base de código.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Eu nunca vi isso antes. O desenvolvedor original está fora de alcance. Portanto, eu tenho que perguntar SO:

Quais são as semânticas por trás de uma interface estática? O que mudaria se eu removesse o static? Por que alguém faria isso?

Mo.
fonte
2
Esta não é uma interface interna: é uma interface aninhada . Inner tem um significado específico em Java.
Marquês de Lorne

Respostas:

293

A palavra-chave estática no exemplo acima é redundante (uma interface aninhada é automaticamente "estática") e pode ser removida sem afetar a semântica; Eu recomendaria que fosse removido. O mesmo vale para "público" nos métodos de interface e "público final" nos campos de interface - os modificadores são redundantes e apenas adicionam confusão ao código-fonte.

De qualquer forma, o desenvolvedor está simplesmente declarando uma interface chamada Foo.Bar. Não há associação adicional com a classe envolvente, exceto que o código que não pode acessar o Foo também não poderá acessar o Foo.Bar. (Do código-fonte - o bytecode ou o reflexo podem acessar o Foo.Bar, mesmo que o Foo seja privado de pacotes!)

É um estilo aceitável criar uma interface aninhada dessa maneira, se você espera que ela seja usada apenas a partir da classe externa, para que você não crie um novo nome de nível superior. Por exemplo:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Jesse Glick
fonte
1
Na resposta de Jesse Glick, o que isso significa: (Do código-fonte - bytecode ou reflexão podem acessar o Foo.Bar, mesmo que o Foo seja privado de pacotes!).
Vasu
No Kaillash, os métodos privados podem ser acessados ​​através da re-conexão (no pacote reflect) e do acesso direto ao bytecode dos arquivos .class gerados.
precisa saber é o seguinte
2
Por "bytecode ... pode acessar o Foo.Bar", quero dizer que uma classe compilada que faz referência ao Foo.Bar pode ser carregada e executada mesmo que não possa fazer referência ao Foo. Isso pode acontecer se a classe tiver sido compilada em um momento anterior quando o Foo fosse público, ou se a classe fosse montada manualmente ou compilada a partir de uma linguagem não Java, etc. O compilador Java, no entanto, verifica o modificador de acesso na classe envolvente mesmo quando o bytecode resultante não se referir a essa classe anexa.
precisa
@Jesse Uma classe estática privada em uma classe privada de nível superior pode ser acessada através de reflexão?
Pacerier
1
@Pacerier: não. Mais precisamente, você pode carregar a classe e inspecionar seus membros, mas não pode instanciar ou chamar métodos nela sem usar setAccessible (true). Em outras palavras, é visível, mas não acessível, via reflexão. Mas se a classe estática aninhada for pública, ela poderá ser acessada por padrão através de reflexão, mesmo que não seja acessada estaticamente (durante a compilação).
Jesse Glick
72

A pergunta foi respondida, mas um bom motivo para usar uma interface aninhada é se sua função estiver diretamente relacionada à classe em que está. Um bom exemplo disso é a Listener. Se você tivesse uma classe Fooe desejasse que outras classes pudessem ouvir eventos nela, poderia declarar uma interface com o nome FooListenerok, mas provavelmente seria mais claro declarar uma interface aninhada e implementar essas outras classes Foo.Listener( uma classe aninhada Foo.Eventnão é ruim junto com isso).

ColinD
fonte
11
Um exemplo típico é java.util.Map.Entry(que é uma interface aninhada em outra interface).
Pa Elo Ebermann
4
Eu sei que esse é um tópico antigo, mas eu preferiria que a classe externa estivesse em seu próprio pacote e quaisquer interfaces suplementares (por exemplo Map.Entry) ou classes também dentro desse pacote. Digo isso porque gosto de manter minhas aulas curtas e objetivas. Além disso, um leitor pode ver quais outras entidades estão relacionadas a uma classe observando as classes no pacote. Eu provavelmente teria um pacote java.collections.mappara mapas. Trata-se de OO e modularidade. java.utiltem muito nele. utilé como common- um cheiro IMO
David Kerr
1
@ Shagy: java.utilcertamente tem muito nele. Dito isso, não acho que seja ideal dividi-lo em pacotes refinados, como você sugere.
colind
1
@DavidKerr Suponha que você chame essa interface java.util.MapEntryfora de seu próprio pacote. Primeiro reflexo: quais são as interfaces relacionadas a essa classe? Eu olho para a classe. A menos que o javadoc esteja vinculado a essa interface, não tenho idéia de sua relação. Além disso, as pessoas não olham para o pacote de importação. Todo mundo tem opiniões próprias. Ninguém está certo, ninguém está errado
Raymond Chenon
@RaymondChenon parte do que eu disse foi colocar o Map e suas classes associadas, por exemplo, Map.Entry em um pacote separado, por exemplo, java.collections.map. Dessa forma, todo o código relacionado ao mapa está em um único local (o pacote) e a classe Map de nível superior não é sobrecarregada. Talvez eu coloque as implementações relacionadas (HashMap, etc) no mesmo pacote.
David Kerr
14

As interfaces dos membros são implicitamente estáticas. O modificador estático no seu exemplo pode ser removido sem alterar a semântica do código. Consulte também a Java Language Specification 8.5.1. Declarações de tipo de membro estático

Bas Leijdekkers
fonte
Esta não é uma 'interface interna': é uma interface aninhada. Inner tem um significado específico em Java.
Marquês de Lorne
@EJP, li vários blogs usando os termos de forma intercambiável. A interface interna existe? Como eles são diferentes da interface aninhada?
Number945
@BreakingBenjamin, interface aninhada significa estática. Existe classe interna e classe aninhada, mas não interface interna. Mesmo assim, deve ser chamado como interface aninhada. Interno - não estático e aninhado é estático.
Sundar Rajan
9

Uma interface interna deve ser estática para ser acessada. A interface não está associada a instâncias da classe, mas à própria classe, portanto, seria acessada com Foo.Bar:

public class Baz implements Foo.Bar {
   ...
}

De muitas maneiras, isso não é diferente de uma classe interna estática.

Clinton N. Dreisbach
fonte
35
Uma interface aninhada é automaticamente estática, independentemente de alguém escrever a palavra-chave ou não.
Pa Elo Ebermann
3
Nós realmente precisamos de uma maneira para a comunidade a votar para aceitar uma resposta diferente: stackoverflow.com/a/74400/632951
Pacerier
@ ClintonN.Dreisbach você pode explicar o que se entende por The interface isn't associated with instances of the class, but with the class itselfmais ainda, eu não consegui-lo
Kasun Siyambalapitiya
6

A resposta de Jesse é próxima, mas acho que existe um código melhor para demonstrar por que uma interface interna pode ser útil. Veja o código abaixo antes de ler. Você consegue descobrir por que a interface interna é útil? A resposta é que a classe DoSomethingAlready pode ser instanciada com qualquer classe que implemente A e C; não apenas a classe concreta Zoo. Obviamente, isso pode ser alcançado mesmo que a CA não seja interna, mas imagine concatenar nomes mais longos (não apenas A e C), e faça isso para outras combinações (por exemplo, A e B, C e B, etc.) e você facilmente veja como as coisas saem de controle. Sem mencionar que as pessoas que revisam sua árvore de origem serão sobrecarregadas por interfaces que são significativas apenas em uma classe.uma interface interna permite a construção de tipos personalizados e melhora seu encapsulamento .

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
user1982892
fonte
2
Isso não faz sentido. A classe Zooque não implementar a interface AC, portanto, os casos de Zoonão pode ser passado para o construtor do DoSomethingAlreadyque espera AC. O fato de ACestender ambos Ae Cnão implica que as classes sejam implementadas Ae Cmagicamente também sejam implementadas AC.
Holger
3

Para responder sua pergunta diretamente, consulte Map.Entry.

Map.Entry

também isso pode ser útil

Entrada do blog Static Nested Inerfaces

Henry B
fonte
4
Este é um exemplo, mas não é realmente uma resposta.
Pa Elo Ebermann
Eu uso o Map.Entry para criar muito objetos "Emparelhar". Está exposto. A implementação do Pair tem duas escolas de pensamento, mas esse não é o ponto aqui. Map.Entry pode ser interno, mas eu o uso fora.
Ravindranath Akila
0

Normalmente, vejo classes internas estáticas. Classes internas estáticas não podem fazer referência às classes que contêm, enquanto classes não estáticas podem. A menos que você esteja enfrentando algumas colisões de pacotes (já existe uma interface chamada Bar no mesmo pacote que o Foo), acho que seria o próprio arquivo. Também poderia ser uma decisão de design para impor a conexão lógica entre Foo e Bar. Talvez o autor pretenda que o Bar seja usado apenas com Foo (embora uma interface interna estática não imponha isso, apenas uma conexão lógica)

basszero
fonte
'Interior estático' é uma contradição em termos. Classes aninhadas são tanto estática ou interior.
Marquês de Lorne
0

Se você mudar a classe Foo para a interface Foo, a palavra-chave "public" no exemplo acima também será redundante porque

A interface definida dentro de outra interface implicitamente public estática.

Danylo Volokh
fonte
Não responder à pergunta
Raining
0

Em 1998, Philip Wadler sugeriu uma diferença entre interfaces estáticas e não estáticas.

Até onde posso ver, a única diferença em tornar uma interface não estática é que agora ela pode incluir classes internas não estáticas; portanto, a alteração não invalidaria nenhum programa Java existente.

Por exemplo, ele propôs uma solução para o Problema da Expressão , que é a incompatibilidade entre expressão como "quanto sua linguagem pode expressar", por um lado, e expressão como "os termos que você está tentando representar em sua linguagem", por outro lado. .

Um exemplo da diferença entre interfaces aninhadas estáticas e não estáticas pode ser visto em seu código de amostra :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Sua sugestão nunca chegou ao Java 1.5.0. Portanto, todas as outras respostas estão corretas: não há diferença para interfaces aninhadas estáticas e não estáticas.

Pindatjuh
fonte
Deve-se notar que tudo isso se refere ao GJ, que era um processador muito antigo para genéricos em Java.
Marquês de Lorne
-1

Em Java, a interface / classe estática permite que a interface / classe seja usada como uma classe de nível superior, ou seja, pode ser declarada por outras classes. Então, você pode fazer:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Sem o estático, o acima falharia ao compilar. A vantagem disso é que você não precisa de um novo arquivo de origem apenas para declarar a interface. Também associa visualmente a interface Bar à classe Foo, já que você precisa escrever Foo.Bar e implica que a classe Foo faz algo com instâncias de Foo.Bar.

Uma descrição dos tipos de classe em Java .

Skizz
fonte
Sem a estática que faz compilação. O 'estático' é redundante.
Marquês de Lorne
@EJP: De acordo com o artigo ao qual vinculei, sem a estática, a classe interna só pode ser usada pela classe proprietária e não por qualquer coisa fora da classe devida. A estática a torna uma classe de nível superior aninhada e pode ser usada por objetos fora do escopo da classe proprietária (pelo menos, de acordo com o artigo). A estática não é totalmente redundante e afeta o escopo da classe interna. Isso se aplica às classes, as interfaces sempre podem agir como objetos de nível superior (é necessário ler as especificações de idioma para verificar). Talvez eu devesse ter separado as partes da interface e da classe da minha resposta (minha má).
Skizz
-6

Estático significa que qualquer parte da classe do pacote (projeto) pode acessá-lo sem usar um ponteiro. Isso pode ser útil ou dificultar dependendo da situação.

O exemplo perfeito das utilidades dos métodos "estáticos" é a classe Math. Todos os métodos em matemática são estáticos. Isso significa que você não precisa sair do seu caminho, criar uma nova instância, declarar variáveis ​​e armazená-las em ainda mais variáveis; basta digitar seus dados e obter um resultado.

A estática nem sempre é útil. Se você estiver comparando casos, por exemplo, convém armazenar dados de várias maneiras diferentes. Você não pode criar três métodos estáticos com assinaturas idênticas. Você precisa de 3 instâncias diferentes, não estáticas e, em seguida, é possível comparar e, se for estático, os dados não serão alterados junto com a entrada.

Os métodos estáticos são bons para retornos únicos e cálculos rápidos ou dados facilmente obtidos.

Vordreller
fonte
1
Eu sei o que significa estática. Eu nunca vi isso em uma interface antes. Portanto, sua pergunta está fora de tópico - desculpe
Mo.
1
Eu acho que a resposta dele é fora de tópico.
precisa saber é o seguinte