【问题标题】:Constructor in an Interface?接口中的构造函数?
【发布时间】:2026-01-28 05:05:02
【问题描述】:

我知道在接口中定义构造函数是不可能的。但我想知道为什么,因为我认为它可能非常有用。

因此,您可以确定为该接口的每个实现都定义了类中的某些字段。

例如考虑以下消息类:

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

如果为这个类定义一个接口,以便我可以有更多的类实现消息接口,我只能定义发送方法而不是构造函数。那么如何确保这个类的每个实现都真正有一个接收器集呢?如果我使用像setReceiver(String receiver) 这样的方法,我不能确定这个方法是否真的被调用了。在构造函数中我可以确保它。

【问题讨论】:

  • 您说“在构造函数中,我可以确保 [此类的每个实现都确实有一个接收器集]。”但是不,你不可能那样做。如果可以定义这样的构造函数,则该参数只会对您的实现者提供强有力的提示——但如果他们愿意,他们可以选择简单地忽略它。
  • @mattb 嗯,那是另一种语言。

标签: java interface


【解决方案1】:

拿一些你描述的东西:

“所以你可以确定一个类中的某些字段是为 这个接口的每个实现。”

"如果为这个类定义一个接口,这样我就可以拥有更多 实现消息接口的类,我只能定义 发送方法而不是构造函数”

...这些要求正是 abstract classes 的用途。

【讨论】:

  • 但请注意@Sebi 描述的用例(从父构造函数调用重载方法)是一个坏主意,正如我在回答中所解释的那样。
  • Matt,这显然是正确的,但抽象类受到单一继承限制,这导致人们寻找其他指定层次结构的方法。
  • 这是事实,可能会解决 Sebi 的直接问题。但是在 Java 中使用接口的原因之一是您不能拥有多重继承。在我无法使我的“事物”成为抽象类的情况下,因为我需要从其他事物继承,问题仍然存在。并不是说我声称有解决方案。
  • @CPerkins 虽然这是真的,但我并不是说简单地使用抽象类就可以解决 Sebi 的用例。如果有的话,最好声明一个定义send() 方法的Message 接口,如果Sebi 希望为Message 接口的实现提供一个“基”类,那么也提供一个AbstractMessage。抽象类不应该取代接口,从来没有试图建议这样做。
  • 明白了,马特。我不是在和你争论,更多的是指出它不是操作人员想要的完全替代品。
【解决方案2】:

当您在接口中允许构造函数时遇到的一个问题来自于同时实现多个接口的可能性。当一个类实现多个定义不同构造函数的接口时,该类必须实现多个构造函数,每个构造函数只满足一个接口,而不满足其他接口。不可能构造一个调用每个构造函数的对象。

或者在代码中:

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

【讨论】:

  • 语言不能通过允许class A implements Named, HashList { A(){HashList(new list()); Named("name");} }之类的东西来做到这一点
  • 如果允许,“接口中的构造函数”最有用的含义是,如果new Set<Fnord>() 可以解释为“给我一些我可以用作Set<Fnord> 的东西”;如果Set<T> 的作者打算将HashSet<T> 用作对其他东西没有特别需求的东西的首选实现,那么接口可以定义new Set<Fnord>() 可以被认为是new HashSet<Fnord>() 的同义词。一个类实现多个接口不会造成任何问题,因为new InterfaceName() 会简单地构造一个由接口指定的类
  • 反论点:您的 A(String,List) 构造函数可能是指定的构造函数,而 A(String)A(List) 可能是调用它的次要构造函数。你的代码不是反例,只是一个糟糕的例子。
  • 为什么要在实现中调用所有构造函数?!是的,如果它用 ctor 实现了更多接口,一个用 String 和一个用 int,它需要两个 ctor - 但可以使用 或 。如果那不适用,则该类根本不实现这两个接口。所以呢!? (虽然接口中没有 ctor 还有其他原因)。
  • @kai 不行,构造实例时需要调用两个接口构造函数。换句话说:在我的示例中,实例同时具有名称和列表,因此每个实例都需要实例化名称和列表。
【解决方案3】:

接口定义了 API 的契约,即 API 的实现者和用户都同意的一组方法。接口没有实例化实现,因此没有构造函数。

您描述的用例类似于抽象类,其中构造函数调用在子类中实现的抽象方法的方法。

这里的固有问题是,在执行基本构造函数时,子对象尚未构造,因此处于不可预测的状态。

总结一下:从父构造函数调用重载方法是不是自找麻烦,引用mindprod

一般来说,您必须避免调用任何 构造函数中的非最终方法。 问题是那个实例 初始化器/变量初始化 在派生类中执行 基础的构造函数之后 类。

【讨论】:

    【解决方案4】:

    您可以尝试的一种解决方法是在您的界面中定义一个getInstance() 方法,以便实现者知道需要处理哪些参数。它不像抽象类那样坚固,但它作为接口提供了更大的灵活性。

    但是,此解决方法确实需要您使用getInstance() 来实例化此接口的所有对象。

    例如

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

    【讨论】:

    • 是否需要创建实例才能访问getInstance方法?
    【解决方案5】:

    接口中只有静态字段,子类创建对象时不需要初始化,接口的方法必须在子类中提供实际实现,所以接口中不需要构造函数。

    第二个原因——子类创建对象的时候调用了父构造函数,但是如果实现的接口不止一个,那么调用接口构造函数的时候就会发生冲突,先调用哪个接口的构造函数

    【讨论】:

      【解决方案6】:

      如果你想确保接口的每个实现都包含特定的字段,你只需需要为你的接口添加该字段的 getter

      interface IMyMessage(){
          @NonNull String getReceiver();
      }
      
      • 不会破坏封装
      • 它会让使用你的接口的每个人都知道Receiver 对象必须以某种方式(通过构造函数或通过setter)传递给类

      【讨论】:

        【解决方案7】:

        接口方法中未引用的依赖项应视为实现细节,而不是接口强制执行的内容。当然也有例外,但作为一项规则,您应该将您的界面定义为预期的行为。给定实现的内部状态不应该是接口的设计问题。

        【讨论】:

          【解决方案8】:

          这是因为接口不允许在其中定义方法体。但是我们应该在同一个类中定义构造函数,因为接口默认具有抽象修饰符来定义所有方法。这就是为什么我们不能在接口中定义构造函数。

          【讨论】:

            【解决方案9】:

            请参阅this question 了解为什么(取自 cmets)。

            如果你真的需要做这样的事情,你可能需要一个抽象基类而不是一个接口。

            【讨论】:

              【解决方案10】:

              这是使用此技术的示例。在这个特定的示例中,代码使用模拟 MyCompletionListener 调用 Firebase,这是一个被屏蔽为抽象类的接口,一个带有构造函数的接口

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

              【讨论】:

              • 如何将private 访问修饰符与interface 一起使用?
              【解决方案11】:

              通常构造函数用于初始化特定类的非静态成员。

              接口没有对象创建,因为只有声明的方法,没有定义的方法。为什么我们不能为声明的方法创建对象 is-object 创建只不过是为非静态成员分配一些内存(在堆内存中)。

              JVM 将为完全开发并准备使用的成员创建内存。基于这些成员,JVM 计算它们需要多少内存并创建内存。

              在声明方法的情况下,JVM 无法计算这些声明方法需要多少内存,因为实现将在未来完成,此时尚未完成。因此接口无法创建对象。

              结论:

              如果不创建对象,就没有机会通过构造函数初始化非静态成员。这就是为什么接口内部不允许构造函数的原因。(因为接口内部没有使用构造函数)

              【讨论】: