Constructor en una interfaz?

148

Sé que no es posible definir un constructor en una interfaz. Pero me pregunto por qué, porque creo que podría ser muy útil.

Por lo tanto, puede estar seguro de que algunos campos de una clase están definidos para cada implementación de esta interfaz.

Por ejemplo, considere la siguiente clase de mensaje:

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
   }
}

Si se define una interfaz para esta clase de modo que pueda tener más clases que implementen la interfaz de mensajes, solo puedo definir el método de envío y no el constructor. Entonces, ¿cómo puedo asegurarme de que cada implementación de esta clase realmente tenga un conjunto de receptores? Si uso un método como setReceiver(String receiver)no puedo estar seguro de que realmente se llama a este método. En el constructor podría asegurarlo.

daniel kullmann
fuente
2
Usted dice "En el constructor podría asegurarme [cada implementación de esta clase realmente tiene un conjunto de receptores]". Pero no, no podrías hacer eso. Siempre que fuera posible definir un constructor de este tipo, el parámetro solo sería una pista importante para sus implementadores, pero podrían elegir simplemente ignorarlo si quisieran.
Julien Silla y
3
@mattb Umm, ese es un idioma diferente.
yesennes

Respuestas:

129

Tomando algunas de las cosas que ha descrito:

"Así que puede estar seguro de que algunos campos de una clase están definidos para cada implementación de esta interfaz".

"Si se define una interfaz para esta clase de modo que pueda tener más clases que implementen la interfaz de mensajes, solo puedo definir el método de envío y no el constructor"

... estos requisitos son exactamente para lo que son las clases abstractas .

mate b
fuente
pero tenga en cuenta que el caso de uso que describe @Sebi (llamar a métodos sobrecargados de constructores principales) es una mala idea como se explica en mi respuesta.
rsp
44
Matt, eso es claramente cierto, pero las clases abstractas sufren de la limitación de herencia única, lo que lleva a las personas a buscar otras formas de especificar jerarquías.
CPerkins
66
Esto es cierto y puede resolver el problema inmediato de Sebi. Pero una razón para usar interfaces en Java es porque no puede tener herencia múltiple. En un caso en el que no puedo hacer de mi "cosa" una clase abstracta porque necesito heredar de otra cosa, el problema persiste. No es que afirme tener una solución.
Jay
77
@CPerkins, si bien esto es cierto, no estoy sugiriendo que el simple uso de una clase abstracta resuelva el caso de uso de Sebi. En todo caso, es mejor declarar una Messageinterfaz que defina el send()método, y si Sebi desea proporcionar una clase "base" para implementaciones de la Messageinterfaz, proporcione también una AbstractMessage. Las clases abstractas no deberían tomar el lugar de las interfaces, nunca intentaron sugerirlo.
mate b
2
Entendido, Matt. No estaba discutiendo contigo, más señalando que no es un reemplazo completo de lo que quiere el operador.
CPerkins
76

Un problema que se presenta cuando se permiten constructores en las interfaces proviene de la posibilidad de implementar varias interfaces al mismo tiempo. Cuando una clase implementa varias interfaces que definen diferentes constructores, la clase tendría que implementar varios constructores, cada uno de los cuales satisface una sola interfaz, pero no las otras. Será imposible construir un objeto que llame a cada uno de estos constructores.

O en 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); 
  }
}
daniel kullmann
fuente
1
No podría haberlo hecho el idioma al permitir cosas comoclass A implements Named, HashList { A(){HashList(new list()); Named("name");} }
mako
1
El significado más útil para un "constructor en una interfaz", si está permitido, sería si se new Set<Fnord>()pudiera interpretar que significa "Dame algo que pueda usar como un Set<Fnord>"; Si el autor Set<T>pretendía HashSet<T>ser la implementación de referencia para cosas que no tenían una necesidad particular de otra cosa, la interfaz podría definirse new Set<Fnord>()como sinónimo de new HashSet<Fnord>(). Para que una clase implemente múltiples interfaces no representaría ningún problema, ya new InterfaceName()que simplemente construiría una clase designada por la interfaz .
supercat
Contraargumento: su A(String,List)constructor podría ser el constructor designado, A(String)y A(List)podría ser uno secundario que lo llame. Su código no es un contraejemplo, solo uno pobre.
Ben Leggiero
¿Por qué llamarías a todos los constructores en una implementación? Sí, si implementa más interfaces con ctor, una con una cadena y otra con un int, necesita dos ctors, pero puede usarse o puede usarse. Si eso no es aplicable, la clase simplemente no implementa ambas interfaces. ¿¡Y qué!? (Sin embargo, hay otras razones para no tener ctor en las interfaces).
kai
@kai No, necesita llamar a ambos constructores de interfaz al construir una instancia. En otras palabras: en mi ejemplo, la instancia tiene un nombre y una lista, por lo que cada instancia necesita crear una instancia tanto del nombre como de la lista.
daniel kullmann
13

Una interfaz define un contrato para una API, que es un conjunto de métodos que acuerdan tanto el implementador como el usuario de la API. Una interfaz no tiene una implementación instanciada, por lo tanto, no tiene un constructor.

El caso de uso que describe es similar a una clase abstracta en la que el constructor llama a un método de un método abstracto que se implementa en una clase secundaria.

El problema inherente aquí es que mientras se ejecuta el constructor base, el objeto hijo aún no se construye y, por lo tanto, en un estado impredecible.

Para resumir: es pedir problemas cuando llama a métodos sobrecargados de constructores principales, para citar mindprod :

En general, debe evitar llamar a cualquier método no final en un constructor. El problema es que la inicialización de instancia / inicialización de variable en la clase derivada se realiza después del constructor de la clase base.

rsp
fuente
6

Una solución que puede intentar es definir un getInstance()método en su interfaz para que el implementador sepa qué parámetros deben manejarse. No es tan sólido como una clase abstracta, pero permite más flexibilidad como una interfaz.

Sin embargo, esta solución requiere que use el getInstance()para instanciar todos los objetos de esta interfaz.

P.ej

public interface Module {
    Module getInstance(Receiver receiver);
}
Lappro
fuente
5

Solo hay campos estáticos en la interfaz que no necesitan inicializarse durante la creación de objetos en la subclase y el método de la interfaz debe proporcionar una implementación real en la subclase. Por lo tanto, no hay necesidad de un constructor en la interfaz.

Segunda razón: durante la creación del objeto de la subclase, se llama al constructor principal.

Satyajit Gami
fuente
3

Si desea asegurarse de que cada implementación de la interfaz contenga un campo específico, simplemente necesita agregar a su interfaz el captador para ese campo :

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • no romperá la encapsulación
  • informará a todos los que usen su interfaz que el Receiverobjeto debe pasar a la clase de alguna manera (ya sea por constructor o por instalador)
Denys Vasylenko
fuente
2

Las dependencias a las que no se hace referencia en los métodos de una interfaz deben considerarse como detalles de implementación, no como algo que la interfaz impone. Por supuesto, puede haber excepciones, pero como regla general, debe definir su interfaz como cuál se espera que sea el comportamiento. El estado interno de una implementación dada no debería ser una preocupación de diseño de la interfaz.

Yishai
fuente
1

Vea esta pregunta para saber por qué (tomado de los comentarios).

Si realmente necesita hacer algo como esto, es posible que desee una clase base abstracta en lugar de una interfaz.

JSB ձոգչ
fuente
1

Esto se debe a que las interfaces no permiten definir el cuerpo del método en él, pero deberíamos tener que definir el constructor en la misma clase que las interfaces tienen por defecto un modificador abstracto para todos los métodos a definir. Es por eso que no podemos definir constructor en las interfaces.

Aasif Ali
fuente
0

Aquí hay un ejemplo usando esta técnica. En este ejemplo específico, el código está haciendo una llamada a Firebase usando un simulacro MyCompletionListenerque es una interfaz enmascarada como una clase abstracta, una interfaz con un constructor

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);
}
ExocetKid
fuente
¿Cómo se puede usar el privatemodificador de acceso interface?
Rudra
0

Generalmente los constructores son para inicializar miembros no estáticos de una clase particular con respecto al objeto.

No hay creación de objetos para la interfaz, ya que solo hay métodos declarados pero no métodos definidos. ¿Por qué no podemos crear objetos para métodos declarados? La creación de objetos es nada más que asignar algo de memoria (en memoria de almacenamiento dinámico) para miembros no estáticos.

JVM creará memoria para los miembros que están completamente desarrollados y listos para usar. Basado en esos miembros, JVM calcula la cantidad de memoria requerida para ellos y crea memoria.

En el caso de los métodos declarados, JVM no puede calcular la cantidad de memoria necesaria para estos métodos declarados, ya que la implementación será en el futuro, lo que no se ha hecho en este momento. entonces la creación de objetos no es posible para la interfaz.

conclusión:

sin la creación de objetos, no hay posibilidad de inicializar miembros no estáticos a través de un constructor. Es por eso que el constructor no está permitido dentro de una interfaz (ya que no hay uso del constructor dentro de una interfaz)

Sai Kumar
fuente