为啥接口变量默认是静态的和最终的?

Posted

技术标签:

【中文标题】为啥接口变量默认是静态的和最终的?【英文标题】:Why are interface variables static and final by default?为什么接口变量默认是静态的和最终的? 【发布时间】:2011-01-26 17:01:03 【问题描述】:

为什么Java中的接口变量默认是static和final的?

【问题讨论】:

你不应该在接口里面放任何变量。 因为接口定义了可以以各种方式实现的合约。变量的值是实现。 当我们知道所有实现接口的类都有一些常量变量(例如字段名称)时,我们当然可以。 将类中的变量作为该类实现的接口的实例是个好主意吗?我以前听说过。 Arun Raaj 的回答(2018 年 4 月 24 日 12:30 回答)和 denis 的评论(2017 年 8 月 17 日 21:02)正确地将多重继承确定为主要问题...跨度> 【参考方案1】:

来自 Philip Shaw 的 Java 界面设计常见问题解答:

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

source

【讨论】:

请注意,抽象类也不能“以它们自己的权利”实例化,它们可以有实例变量。 这种对static 修饰符的解释完全是虚假的。一个类的公共实例变量是它的接口的一部分,没有理由不应该在 Java interface 中抽象它们,就像实例方法一样。不能直接实例化 Java interface 并不重要 - 您仍然可以拥有实现 interface 的类的实例,并且要求它们具有某个公共实例变量是明智的。至于final 的部分,根本没有提供任何解释——它只是描述了final 的含义。 上面的引用在上下文中更好。它给出的原因是“接口变量旨在成为 Java 常量”。引用只是详细说明了为什么这样的常量是静态的和最终的。确实如此,但真正的问题是:为什么不允许将变量作为实际接口 的一部分(即指定必须在实现类中出现的非私有成员的名称和类型)。如果他们想要特殊的“接口常量”,他们可以使用新语法,或者只是决定在接口中实际定义的任何变量都是接口常量。 接口不能有实例变量以避免多重继承的状态问题。见docs.oracle.com/javase/tutorial/java/IandI/…。由于同样的原因,一个类不能扩展多个类。 默认方法是如何引入的,它们确实有实例,但不支持实例变量...【参考方案2】:

public:用于所有类的可访问性,就像接口中的方法一样

静态:由于接口不能有对象,可以使用 interfaceName.variableName 来引用它,也可以直接在实现它的类中使用 variableName。

final:使它们成为常量。如果 2 个类实现了相同的接口,并且你赋予它们两个改变值的权利,那么 var 的当前值就会发生冲突,这就是为什么只允许一次初始化的原因。

此外,所有这些修饰符对于接口都是隐含的,您实际上不需要指定其中任何一个。

【讨论】:

【参考方案3】:

由于接口没有直接对象,访问它们的唯一方法是使用类/接口,因此如果接口变量存在,它应该是静态的,否则外界根本无法访问它.现在因为它是静态的,它只能保存一个值,任何实现它的类都可以改变它,因此它会一团糟。

因此,如果有一个接口变量,它将是隐式静态的、最终的并且显然是公共的!!!

【讨论】:

当然,如果在 Java interface 中允许,实例变量将是可访问的。一个类将实现接口,声明实例变量(根据接口的要求)。它的构造函数(或其他方法)设置实例变量。当类的实例被实例化时,您将能够访问其实例变量。 Java 允许带有主体的静态方法存在于接口中。那些可以访问静态变量。他们只是不能改变它们,这意味着静态函数不能存储任何数据【参考方案4】:

这不是一个哲学答案,而是一个实际的答案)。 static 修饰符的要求很明显,其他人已经回答了。基本上,由于接口无法实例化,因此访问其字段的唯一方法是使它们成为类字段 --static

interface 字段自动变为final(常量)背后的原因是为了防止不同的实现意外更改接口变量的值,从而无意中影响其他实现的行为。想象一下下面的场景,其中 interface 属性没有被 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() 时,NuclearAction 的行为可能会受到另一个类 CleanAction 的影响(在同一个线程或其他线程中),在这种情况下可能是灾难性的(语义上就是这样)。

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

【讨论】:

【参考方案5】:

因为其他任何东西都是实现的一部分,接口不能包含任何实现。

【讨论】:

那么final的原因是什么。 表示它是一个常数。 Java 没有 const 关键字。 static final 是您声明常量的方式。 从 Java 8 开始,它们可以包含一个实现,但如果您不需要向后兼容性,强烈建议不要使用它。 :)【参考方案6】:
public interface A
    int x=65;

public interface B
    int x=66;

public class D implements A,B 
    public static void main(String[] a)
        System.out.println(x); // which x?
    

这是解决方案。

System.out.println(A.x); // done

我认为这是接口变量是静态的原因之一。

不要在接口内声明变量。

【讨论】:

事实上,如果没有规范“A.x”,它甚至不会编译”,因此在接口中使用变量(隐含地是公共静态最终变量)实际上是安全的。 我不同意这个答案,因为@Marco 说它甚至不会编译。到目前为止我没有发现任何其他缺点,也许只是你没有看到在实际上是静态和最终的变量之前写了static final【参考方案7】:

静态 - 因为接口不能有任何实例。和 final - 因为我们不需要更改它。

【讨论】:

"我们不需要" == "我们不允许",不要混淆意思。【参考方案8】:

因为:

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

Final:这样我们就不应该有变量的模棱两可的值(钻石问题 - 多重继承)。

并且根据文档接口是合同而不是实现。

参考:Abhishek Jain 在 quora 上的 answer

【讨论】:

【参考方案9】:

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”关键字意味着只有扩展类可以访问这些方法和变量。)

斯派罗

【讨论】:

【参考方案10】:

接口:系统需求服务。

在接口中,变量默认由public,static,final访问修饰符赋值。 因为:

public : 有时接口可能会放在其他包中。所以它需要从项目中的任何地方访问变量。

静态:这样不完整的类不能创建对象。所以在项目中我们需要访问没有对象的变量,这样我们就可以在interface_filename.variable_name的帮助下访问

final : 假设一个接口由多个类实现,所有类都尝试访问和更新接口变量。因此它会导致数据变化不一致并影响其他所有类。所以需要用final声明访问修饰符。

【讨论】:

【参考方案11】:

接口是两方之间不变的契约,刻在石头上,因此是最终的。见Design by Contract。

【讨论】:

【参考方案12】:

Java 中,接口不允许您声明任何实例变量。将接口中声明的变量用作实例变量将返回编译时错误。

您可以使用不同于实例变量的static final 声明一个常量变量。

【讨论】:

这是完全错误的。除非您将其设为私有或受保护,否则编译器不会抱怨。正如其他人已经提到的那样,在幕后,它们被转换为公共静态决赛。我想这很明显为什么。因为接口是为了指示行为,而不是状态。【参考方案13】:

接口可以由任何类实现,如果该值被其中一个实现类更改,那么将会误导其他实现类。接口基本上是组合两个相关但不同的实体的引用。因此,接口内的声明变量将隐含地是最终的并且也是静态的,因为接口不能被实例化。

【讨论】:

【参考方案14】:

想象一个 Web 应用程序,其中您定义了接口并由其他类实现它。由于您无法创建接口实例来访问您需要具有静态关键字的变量。由于它是静态的,因此值的任何更改都将反映到已实现它的其他实例。因此,为了防止它发生,我们将它们定义为 final。

【讨论】:

【参考方案15】:

刚刚在Eclipse中试过,interface中的变量默认是final的,所以不能改。与父类相比,变量肯定是可变的。为什么?从我的角度来看,类中的变量是一个属性,将由孩子继承,孩子可以根据自己的实际需要进行更改。相反,接口只定义行为,不定义属性。在接口中放入变量的唯一原因是将它们用作与该接口相关的常量。但是,根据以下摘录,这不是一个好习惯:

“在接口中放置常量在 Java 早期是一种流行的技术,但现在许多人认为它是一种令人反感的接口使用方式,因为接口应该处理对象提供的服务,而不是它的数据。同样,类使用的常量通常是实现细节,但将它们放在接口中会将它们提升为类的公共 API。”

我也尝试过放置静态或不放置根本没有区别。代码如下:

public interface Addable 
    static int count = 6;

    public int add(int i);



public class Impl implements Addable 

    @Override
    public int add(int i) 
        return i+count;
    


public class Test 

    public static void main(String... args) 
        Impl impl = new Impl();

        System.out.println(impl.add(4));
    

【讨论】:

以上是关于为啥接口变量默认是静态的和最终的?的主要内容,如果未能解决你的问题,请参考以下文章

为啥接口方法不能是“静态的”和“最终的”?

2016-11-19 继续努力

java接口成员变量和方法默认修饰符

java接口中的成员方法和成员变量

C语言所有变量都默认为auto,全局变量不能为auto,那全局变量默认为啥?

JDK8新特性:接口的静态方法和默认方法