接口中的构造函数?

时间:2010-05-10 15:41:12

标签: java interface

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

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

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

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

11 个答案:

答案 0 :(得分:121)

采取你所描述的一些事情:

  

“所以你可以确定一个类中的某些字段是为了定义的   这个界面的每一个实现。“

     

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

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

答案 1 :(得分:68)

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

或代码:

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

答案 2 :(得分:10)

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

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

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

总结一下:从父构造函数调用重载方法时引发问题,引用mindprod

  

一般来说,你必须避免打电话   构造函数中的非final方法。   问题是那个例子   初始化/变量初始化   在派生类中执行   之后基础的构造函数   类。

答案 3 :(得分:4)

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

第二个原因 - 在子类的对象创建过程中,父构造函数被调用。但是如果实现了多个接口,那么在调用接口构造函数时会发生冲突,关于哪个接口的构造函数将首先调用

答案 4 :(得分:4)

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

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

E.g。

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

答案 5 :(得分:2)

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

答案 6 :(得分:2)

如果您想确保界面的每个实现都包含特定字段,您只需需要向您的界面添加该字段的getter

spark-submit --master yarn --deploy-mode cluster --class sparkhbase.BulkLoadAsKeyValue3 --driver-cores 8 --driver-memory 11g --executor-cores 4 --executor-memory 9g /home/myuser/app.jar
  • 它不会破坏封装
  • 它会让所有使用你的界面的人知道interface IMyMessage(){ @NonNull String getReceiver(); } 对象必须以某种方式传递给类(通过构造函数或者通过setter)

答案 7 :(得分:1)

请参阅this question了解 why (摘自评论)。

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

答案 8 :(得分:1)

这是因为接口不允许在其中定义方法体。但是我们必须在接口具有相同类的构造函数中默认使用abstract modifier来定义所有方法。这就是我们无法在接口中定义构造函数的原因。

答案 9 :(得分: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); } 是一个被屏蔽为抽象类的接口,一个带有构造函数的接口

@IBOutlet var superView: UIView!
@IBOutlet var Back: UIView!
@IBOutlet var Front: UIView!

@IBAction func Flip(_ sender: AnyObject){

    self.Front.isHidden = false
    self.Front.alpha = 0.1
    UIView.transition(with: superView, duration: 0.5, options: .transitionFlipFromLeft, animations: {
        self.Back.alpha = 0.1
        self.Front.alpha = 1.0
    }) { (isCompleted) in
        self.Back.isHidden = true
    }
}

@IBAction func Flip1(_ sender: AnyObject){

    self.Back.isHidden = false
    self.Back.alpha = 0.1
    UIView.transition(with: superView, duration: 0.5, options: .transitionFlipFromRight, animations: {
        self.Front.alpha = 0.1
        self.Back.alpha = 1.0
    }) { (isCompleted) in
        self.Front.isHidden = true
    }
}

答案 10 :(得分:0)

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

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

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

由于声明的方法很少,JVM无法计算这些声明的方法需要多少内存,因为将来实现将是这样做的。因此,界面无法创建对象。

结论:

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