接口中的构造函数?


148

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

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

例如,考虑以下消息类:

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

如果为该类定义一个接口,以便我可以有更多实现消息接口的类,则只能定义send方法,而不能定义构造函数。那么,如何确保此类的每个实现都确实有一个接收者集?如果我使用类似的方法,setReceiver(String receiver)则不能确定是否真的调用了该方法。在构造函数中,我可以确保这一点。



2
您说:“在构造函数中,我可以确保[该类的每个实现确实都有一个接收者集]。” 但是不,你不可能那样做。如果可以定义这样的构造函数,则该参数只会对您的实现者有很强的提示作用-但是,如果愿意,他们可以选择忽略它。
朱利安·席兰

3
@mattb嗯,那是另一种语言。
yesennes '16

Answers:


129

接受您描述的一些内容:

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

“如果为此类定义一个接口,以便我可以有更多的类来实现消息接口,则只能定义send方法,而不能定义构造函数”

...这些要求正是抽象类的用途。


但是请注意,用例@Sebi描述(从父构造函数调用重载方法)是一个坏主意,如我的回答所述。
rsp 2010年

44
马特(Matt),这显然是对的,但是抽象类受到单继承限制的困扰,这使人们开始考虑其他指定层次结构的方式。
CPerkins 2010年

6
这是事实,并可能解决塞比的紧迫问题。但是在Java中使用接口的原因之一是因为您不能具有多个继承。如果由于需要继承其他东西而无法使“事物”成为抽象类,那么问题仍然存在。并非我声称有解决方案。
杰伊(Jay)

7
@CPerkins虽然这是事实,但我不建议仅使用抽象类即可解决Sebi的用例。如果有的话,最好声明一个Message定义该send()方法的接口,如果Sebi希望为该Message接口的实现提供一个“基”类,则也提供一个AbstractMessage。抽象类不应代替接口,也从未试图建议这样做。
马特b 2010年

2
明白了,马特。我并不是在和您争论,而是要指出,这并不能完全替代操作人员想要的东西。
CPerkins 2010年

76

允许在接口中使用构造函数时遇到的问题是可能同时实现多个接口。当一个类实现多个定义不同构造函数的接口时,该类将必须实现多个构造函数,每个构造函数仅满足一个接口,而不满足其他接口。构造一个调用这些构造函数的对象是不可能的。

或在代码中:

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
语言无法通过允许这样的方式来做到这一点class A implements Named, HashList { A(){HashList(new list()); Named("name");} }
mako

1
如果允许,“接口中的构造函数”最有用的含义是new Set<Fnord>()可以解释为“给我一些我可以用作的东西Set<Fnord>”;如果作者Set<T>打算HashSet<T>成为对其他事物没有特殊需求的事物的实现的实现,则new Set<Fnord>()可以将接口定义为new HashSet<Fnord>()。对于一个类,实现多个接口不会造成任何问题,因为new InterfaceName()它将仅构造由interface指定的类。
supercat 2014年

反议:您的A(String,List)构造函数可以是指定的构造函数,A(String)A(List)可以是调用它的辅助构造函数。您的代码不是一个反例,只是一个糟糕的例子。
Ben Leggiero

为什么要在实现中调用所有构造函数?是的,如果它使用ctor实现更多的接口,一个使用String来实现,一个使用int来实现,则需要两个ctor-但是可以使用一个,也可以使用。如果那不适用,则该类根本不会同时实现这两个接口。所以呢!?(尽管如此,还有其他原因导致接口中没有ctor)。

@kai不,在构造实例时,它需要同时调用两个接口构造函数。换句话说:在我的示例中,实例同时具有名称和列表,因此每个实例都需要实例化名称和列表。
丹尼尔·库尔曼

13

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

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

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

总结一下:当您从父构造函数调用重载方法时,是否会引起麻烦,并引用mindprod

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


6

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

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

例如

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

5

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

第二个原因是在创建子类的对象期间调用了父构造函数。但是,如果实现了多个接口,则在调用接口构造函数期间会发生冲突,即先调用哪个接口的构造函数


3

如果要确保该接口的每个实现都包含特定字段,则只需在该接口中添加该字段的getter即可

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

2

接口方法中未引用的依赖项应被视为实现细节,而不是接口强制执行的事情。当然可以有例外,但是通常,您应该将接口定义为预期的行为。给定实现的内部状态不应该是接口的设计问题。


1

这个问题原因(从注释拍摄)。

如果确实需要执行此类操作,则可能需要抽象的基类而不是接口。


1

这是因为接口不允许在其中定义方法主体,但是我们必须在与接口默认具有抽象修饰符的所有类中定义的构造函数相同的类中定义构造函数。这就是为什么我们不能在接口中定义构造函数的原因。


0

这是使用该技术的示例。在此特定示例中,代码使用模拟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
Rudra

0

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

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

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

对于已声明的方法,JVM无法计算这些已声明的方法所需的内存量,因为将来实现将无法实现。因此无法为接口创建对象。

结论:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.