接口中的构造函数? ?

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

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

例如,请考虑以下消息类:

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)这样的方法,我不能确定这个方法是否真的被调用。在构造函数中,我可以确保它。

216896 次浏览

采取一些你所描述的东西:

”因此可以确定类中的某些字段定义为 这个界面的每一个实现”

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

这些要求正是 抽象类的目的。

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

请参阅 这个问题了解 为什么(摘自注释)。

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

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

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

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

总结一下: 当你从父构造函数中调用重载方法时,它是不是自找麻烦,引用 脑电波:

一般情况下,您必须避免调用任何 构造函数中的非 final 方法。 问题是那个实例 初始化程序/变量初始化 在派生类中执行 在基函数的构造函数 之后 同学们。

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

或者用密码:

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

这是因为接口不允许在. 中定义方法体,但是我们必须在同一个类中定义构造函数,就像默认情况下接口对所有要定义的方法都有抽象修饰符一样。这就是为什么我们不能在接口中定义构造函数。

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

第二个原因——在子类的对象创建过程中,父构造函数被调用。但是如果实现了不止一个接口,那么在调用接口构造函数期间将发生冲突,以决定哪个接口的构造函数将首先调用

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

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

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

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

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

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

例如。

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

一般来说,构造函数用于初始化特定类的非静态成员。

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

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

对于声明的方法,JVM 无法计算这些声明的方法将需要多少内存,因为实现将在未来完成,而现在还没有完成。所以接口不能创建对象。

结论:

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