Construtor em uma interface?


148

Eu sei que não é possível definir um construtor em uma interface. Mas estou me perguntando por que, porque acho que poderia ser muito útil.

Portanto, você pode ter certeza de que alguns campos de uma classe são definidos para todas as implementações dessa interface.

Por exemplo, considere a seguinte classe de mensagem:

public class MyMessage {

   public MyMessage(String receiver) {
      this.receiver = receiver;
   }

   private String receiver;

   public void send() {
      //some implementation for sending the mssage to the receiver
   }
}

Se definir uma interface para esta classe para que eu possa ter mais classes que implementam a interface da mensagem, só posso definir o método send e não o construtor. Então, como posso garantir que toda implementação dessa classe realmente tenha um receptor definido? Se eu usar um método como esse, setReceiver(String receiver)não posso ter certeza de que esse método seja realmente chamado. No construtor, eu poderia garantir isso.



2
Você diz "No construtor, eu poderia garantir [toda implementação dessa classe realmente tem um conjunto de receptores]". Mas não, você não poderia fazer isso. Desde que fosse possível definir um construtor desse tipo, o parâmetro seria apenas uma dica forte para seus implementadores - mas eles poderiam optar por simplesmente ignorá-lo, se quisessem.
Julien Silland

3
@mattb Umm, esse é um idioma diferente.
yesennes 23/01

Respostas:


129

Tomando algumas das coisas que você descreveu:

"Portanto, você pode ter certeza de que alguns campos de uma classe são definidos para todas as implementações dessa interface."

"Se um definir uma interface para esta classe para que eu possa ter mais classes que implementam a interface da mensagem, só posso definir o método send e não o construtor"

... esses requisitos são exatamente para que servem as classes abstratas .


mas observe que o caso de uso que o @Sebi descreve (chamando métodos sobrecarregados de construtores-pai) é uma má ideia, conforme explicado na minha resposta.
Rsp

44
Matt, isso é claramente verdade, mas as classes abstratas sofrem com a limitação de herança única, que leva as pessoas a procurar outras maneiras de especificar hierarquias.
CPerkins

6
Isso é verdade e pode resolver o problema imediato de Sebi. Mas uma razão para usar interfaces em Java é porque você não pode ter herança múltipla. Em um caso em que não posso fazer da minha "coisa" uma classe abstrata porque preciso herdar de outra coisa, o problema permanece. Não que eu afirme ter uma solução.
Jay

7
@CPerkins enquanto isso é verdade, não estou sugerindo que o simples uso de uma classe abstrata resolva o caso de uso de Sebi. Se for o caso, é melhor declarar uma Messageinterface que defina o send()método, e se o Sebi desejar fornecer uma classe "base" para implementações da Messageinterface, forneça AbstractMessagetambém. As classes abstratas não devem substituir as interfaces, nunca tentaram sugerir isso.
matt b

2
Entendido, Matt. Eu não estava discutindo com você, mais apontando que não é um substituto completo para o que a operação quer.
CPerkins

76

Um problema que você obtém ao permitir construtores em interfaces vem da possibilidade de implementar várias interfaces ao mesmo tempo. Quando uma classe implementa várias interfaces que definem diferentes construtores, a classe precisa implementar vários construtores, cada um satisfazendo apenas uma interface, mas não os outros. Será impossível construir um objeto que chame cada um desses construtores.

Ou no código:

interface Named { Named(String name); }
interface HasList { HasList(List list); }

class A implements Named, HasList {

  /** implements Named constructor.
   * This constructor should not be used from outside, 
   * because List parameter is missing
   */
  public A(String name)  { 
    ...
  }

  /** implements HasList constructor.
   * This constructor should not be used from outside, 
   * because String parameter is missing
   */
  public A(List list) {
    ...
  }

  /** This is the constructor that we would actually 
   * need to satisfy both interfaces at the same time
   */ 
  public A(String name, List list) {
    this(name);
    // the next line is illegal; you can only call one other super constructor
    this(list); 
  }
}

1
Não foi possível a linguagem ter feito isso ao permitir que coisas comoclass A implements Named, HashList { A(){HashList(new list()); Named("name");} }
mako

1
O significado mais útil para um "construtor em uma interface", se permitido, seria se new Set<Fnord>()pudesse ser interpretado como "Dê-me algo que eu possa usar como Set<Fnord>"; se o autor de Set<T>pretender HashSet<T>ser a implementação preferencial para coisas que não tinham uma necessidade específica de outra coisa, a interface poderia definir new Set<Fnord>()poderia ser considerada sinônimo new HashSet<Fnord>(). Para uma classe implementar várias interfaces, isso não apresentaria nenhum problema, pois new InterfaceName()simplesmente construiria uma classe designada pela interface .
22714

Contra-argumento: seu A(String,List)construtor pode ser o construtor designado A(String)e A(List)pode ser secundário que o chama. Seu código não é um contra-exemplo, apenas um código ruim.
Ben Leggiero

Por que você chamaria todos os construtores em uma implementação ?! Sim, se implementar mais interfaces com ctor, um com uma String e outro com um int, ele precisará de dois ctors - mas um ou outro poderá ser usado. Se isso não for aplicável, a classe simplesmente não implementa as duas interfaces. E daí!? (existem outras razões para não ter um ctor nas interfaces).
kai

@kai Não, ele precisa chamar os dois construtores de interface ao construir uma instância. Em outras palavras: no meu exemplo, a instância possui um nome e uma lista, portanto, cada instância precisa instanciar o nome e a lista.
Daniel kullmann 08/04/19

13

Uma interface define um contrato para uma API, que é um conjunto de métodos com os quais o implementador e o usuário da API concordam. Uma interface não possui uma implementação instanciada, portanto, nenhum construtor.

O caso de uso que você descreve é ​​semelhante a uma classe abstrata na qual o construtor chama um método de um método abstrato que é implementado em uma classe filho.

O problema inerente aqui é que, enquanto o construtor de base está sendo executado, o objeto filho ainda não foi construído e, portanto, em um estado imprevisível.

Resumindo: é pedir problemas quando você chama métodos sobrecarregados de construtores-pai, para citar mindprod :

Em geral, você deve evitar chamar métodos não finais em um construtor. O problema é que inicializadores de instância / inicialização de variável na classe derivada são executados após o construtor da classe base.


6

Uma solução alternativa que você pode tentar é definir um getInstance()método em sua interface para que o implementador esteja ciente de quais parâmetros precisam ser manipulados. Não é tão sólido quanto uma classe abstrata, mas permite mais flexibilidade do que uma interface.

No entanto, esta solução alternativa exige que você use o getInstance()para instanciar todos os objetos dessa interface.

Por exemplo

public interface Module {
    Module getInstance(Receiver receiver);
}

5

Existem apenas campos estáticos na interface que não precisam ser inicializados durante a criação do objeto na subclasse e o método da interface deve fornecer implementação real na subclasse. Portanto, não há necessidade de construtor na interface.

Segunda razão: durante a criação do objeto da subclasse, o construtor pai é chamado. Mas, se houver mais de uma interface implementada, ocorrerá um conflito durante a chamada do construtor da interface sobre qual construtor da interface chamará primeiro


3

Se você deseja garantir que todas as implementações da interface contenham campos específicos, basta adicionar à sua interface o getter para esse campo :

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • não vai quebrar o encapsulamento
  • informará a todos que usam sua interface que o Receiverobjeto deve ser passado para a classe de alguma forma (por construtor ou setter)

2

Dependências que não são referenciadas nos métodos de uma interface devem ser consideradas como detalhes de implementação, não como algo que a interface impõe. É claro que pode haver exceções, mas, como regra, você deve definir sua interface como qual é o comportamento esperado. O estado interno de uma determinada implementação não deve ser uma preocupação de design da interface.


1

Veja esta pergunta para saber o porquê (retirado dos comentários).

Se você realmente precisar fazer algo assim, convém uma classe base abstrata em vez de uma interface.


1

Isso ocorre porque as interfaces não permitem definir o corpo do método nele. Mas devemos definir o construtor na mesma classe que as interfaces, por padrão, modificador abstrato para todos os métodos a serem definidos. É por isso que não podemos definir construtor nas interfaces.


0

Aqui está um exemplo usando esta técnica. Neste exemplo específico, o código está fazendo uma chamada para o Firebase usando uma simulação MyCompletionListenerque é uma interface mascarada como uma classe abstrata, uma interface com um construtor

private interface Listener {
    void onComplete(databaseError, databaseReference);
}

public abstract class MyCompletionListener implements Listener{
    String id;
    String name;
    public MyCompletionListener(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

private void removeUserPresenceOnCurrentItem() {
    mFirebase.removeValue(child("some_key"), new MyCompletionListener(UUID.randomUUID().toString(), "removeUserPresenceOnCurrentItem") {
        @Override
        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {

        }
    });
    }
}

@Override
public void removeValue(DatabaseReference ref, final MyCompletionListener var1) {
    CompletionListener cListener = new CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (var1 != null){
                        System.out.println("Im back and my id is: " var1.is + " and my name is: " var1.name);
                        var1.onComplete(databaseError, databaseReference);
                    }
                }
            };
    ref.removeValue(cListener);
}

Como você pode usar o privatemodificador de acesso interface?
quer

0

Geralmente os construtores são para inicializar membros não estáticos de uma classe específica em relação ao objeto.

Não há criação de objeto para interface, pois existem apenas métodos declarados, mas não métodos definidos. O motivo pelo qual não podemos criar objetos para métodos declarados é que a criação de objetos nada mais é do que alocar memória (na memória heap) para membros não estáticos.

A JVM criará memória para membros totalmente desenvolvidos e prontos para uso. Com base nesses membros, a JVM calcula a quantidade de memória necessária para eles e cria memória.

No caso de métodos declarados, a JVM não consegue calcular a quantidade de memória necessária para esses métodos declarados, pois a implementação será no futuro, o que não será feito até o momento. portanto, a criação de objetos não é possível para a interface.

conclusão:

sem a criação do objeto, não há chance de inicializar membros não estáticos por meio de um construtor. É por isso que o construtor não é permitido dentro de uma interface. (como não há uso do construtor dentro de uma interface)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.