为什么在Java中接口变量默认是静态的和最终的?


当前回答

(这不是一个哲学上的答案,而是一个更实际的答案)。对静态修饰符的要求是显而易见的,这一点已经被其他人回答了。基本上,由于接口不能被实例化,访问它的字段的唯一方法是使它们成为一个类字段——静态的。

接口字段自动变成final(常量)的原因是为了防止不同的实现意外地改变接口变量的值,这可能会无意中影响其他实现的行为。想象一下下面的场景,接口属性没有被Java显式地变成final:

public interface Actionable {
    public static boolean isActionable = false;

    public void performAction();
}

public NuclearAction implements Actionable {

    public void performAction() {
        // Code that depends on isActionable variable
        if (isActionable) {
            // Launch nuclear weapon!!!
        }
    }
}

现在,想想如果实现Actionable的另一个类改变了接口变量的状态会发生什么:

public CleanAction implements Actionable  {

    public void performAction() {
        // Code that can alter isActionable state since it is not constant
        isActionable = true;
    }
}

如果这些类是由类加载器在单个JVM中加载的,那么当CleanAction的performAction()在CleanAction的执行之后(在同一个线程中或以其他方式)被调用时,核动作的行为可能会受到另一个类CleanAction的影响,在这种情况下,这可能是灾难性的(从语义上来说)。

由于我们不知道接口的每个实现将如何使用这些变量,因此它们必须隐式地为final。

其他回答

(这不是一个哲学上的答案,而是一个更实际的答案)。对静态修饰符的要求是显而易见的,这一点已经被其他人回答了。基本上,由于接口不能被实例化,访问它的字段的唯一方法是使它们成为一个类字段——静态的。

接口字段自动变成final(常量)的原因是为了防止不同的实现意外地改变接口变量的值,这可能会无意中影响其他实现的行为。想象一下下面的场景,接口属性没有被Java显式地变成final:

public interface Actionable {
    public static boolean isActionable = false;

    public void performAction();
}

public NuclearAction implements Actionable {

    public void performAction() {
        // Code that depends on isActionable variable
        if (isActionable) {
            // Launch nuclear weapon!!!
        }
    }
}

现在,想想如果实现Actionable的另一个类改变了接口变量的状态会发生什么:

public CleanAction implements Actionable  {

    public void performAction() {
        // Code that can alter isActionable state since it is not constant
        isActionable = true;
    }
}

如果这些类是由类加载器在单个JVM中加载的,那么当CleanAction的performAction()在CleanAction的执行之后(在同一个线程中或以其他方式)被调用时,核动作的行为可能会受到另一个类CleanAction的影响,在这种情况下,这可能是灾难性的(从语义上来说)。

由于我们不知道接口的每个实现将如何使用这些变量,因此它们必须隐式地为final。

static -因为接口不能有任何实例。最后,因为我们不需要改变它。

因为:

静态:因为我们不能有接口的对象,所以我们应该避免使用对象级成员变量,而应该使用类级变量,即静态。

最后:这样变量的值就不会有歧义(Diamond问题-多重继承)。

根据文档,接口是契约而不是实现。

参考:Abhishek Jain在quora上的回答

摘自Philip Shaw的Java接口设计常见问题解答:

接口变量是静态的,因为Java接口不能自己实例化;变量的值必须在不存在实例的静态上下文中赋值。最后一个修饰符确保分配给接口变量的值是一个真正的常量,不能被程序代码重新分配。

Java不允许在接口中定义抽象变量和/或构造函数。解决方案:简单地在你的接口和你的实现之间挂起一个抽象类,它只像这样扩展抽象类:

 public interface IMyClass {

     void methodA();
     String methodB();
     Integer methodC();

 }

 public abstract class myAbstractClass implements IMyClass {
     protected String varA, varB;

     //Constructor
     myAbstractClass(String varA, String varB) {
         this.varA = varA;
         this.varB = VarB;
     }

     //Implement (some) interface methods here or leave them for the concrete class
     protected void methodA() {
         //Do something
     }

     //Add additional methods here which must be implemented in the concrete class
     protected abstract Long methodD();

     //Write some completely new methods which can be used by all subclasses
     protected Float methodE() {
         return 42.0;
     }

 }

 public class myConcreteClass extends myAbstractClass {

     //Constructor must now be implemented!
     myClass(String varA, String varB) {
         super(varA, varB);
     }

     //All non-private variables from the abstract class are available here
     //All methods not implemented in the abstract class must be implemented here

 }

如果您确定以后不希望将抽象类与其他接口一起实现,也可以使用没有任何接口的抽象类。请注意,你不能创建一个抽象类的实例,你必须先扩展它。

(“protected”关键字意味着只有扩展类才能访问这些方法和变量。)

斯派罗