为什么继承类静态方法而不继承接口静态方法?

Rad*_*ugh 47 java inheritance static-methods interface java-8

我知道在Java中,静态方法就像实例方法一样继承,不同之处在于,当重新声明它们时,父实现被隐藏而不是被覆盖.好吧,这很有道理.但是,Java教程指出了这一点

接口中的静态方法永远不会被继承.

为什么?常规和接口静态方法有什么区别?

当我说静态方法可以继承时,让我澄清一下我的意思:

class Animal {
    public static void identify() {
        System.out.println("This is an animal");
    }
}
class Cat extends Animal {}

public static void main(String[] args) {
    Animal.identify();
    Cat.identify(); // This compiles, even though it is not redefined in Cat.
}
Run Code Online (Sandbox Code Playgroud)

然而,

interface Animal {
    public static void identify() {
        System.out.println("This is an animal");
    }
}
class Cat implements Animal {}

public static void main(String[] args) {
    Animal.identify();
    Cat.identify(); // This does not compile, because interface static methods do not inherit. (Why?)
}
Run Code Online (Sandbox Code Playgroud)

agb*_*nfo 19

这是我的猜测.

因为Cat如果Cat 扩展 只能扩展一个类,Animal那么Cat.identify只有一个含义.Cat可以实现多个接口,每个接口都可以具有静态实现.因此,编译器不知道选择哪一个?

但是,正如作者所指出的,

Java已经存在此问题,使用默认方法.如果两个接口声明默认的void identify(),那么使用哪一个?这是一个编译错误,你必须实现一个重写方法(可能只是Animal.super.identify()).所以Java已经解决了这个问题的默认方法 - 为什么不为静态方法?

如果我再次猜测,我会说default实施是Catvtable的一部分.用static它不可能.主要功能必须绑定到某些东西.在编译时Cat.identify可以Animal.identify由编译器替换,但如果Cat重新编译,代码将不匹配现实,但不包含main的类.


But*_*ass 14

Java 8之前,您无法static在一个中定义方法interface.在这个问题中对此进行了大量讨论.我将参考这个答案(由用户@JamesA.Rosen),为什么Java设计者可能最初不想要static方法interface:

这里有一些问题在起作用.第一个是在不定义静态方法的情况下声明静态方法的问题.这是两者之间的区别

public interface Foo {
  public static int bar();
}
Run Code Online (Sandbox Code Playgroud)

public interface Foo {
  public static int bar() {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

Java也不允许,但它可以允许第二个.由于Espo提到的原因,第一个是不可能的:你不知道哪个实现类是正确的定义.

Java可以允许后者,只要它将Interfaces视为第一类对象.Ruby的模块大致相当于Java的接口,它允许:

module Foo
  def self.bar
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

但是,自Java 8发布以来,您实际上可以在其中添加defaultstatic方法interface.

我将在这里引用很多这个来源.这是最初的问题:

Java的界面语言功能允许您使用抽象方法声明接口,并在实现接口的类中提供这些方法的实现.您需要实现每种方法,这在有许多方法需要实施时很麻烦.此外,在发布界面后,您无法在不破坏源和二进制兼容性的情况下向其添加新的抽象方法.

这是Java 8提供的解决方案default:

Java 8通过改进接口以支持默认和静态方法来解决这些问题.默认方法是在接口中定义的实例方法,其方法头以default关键字开头; 它还提供了一个代码体.实现接口的每个类都继承接口的默认方法并可以覆盖它们

并为static:

静态方法是一种与定义它的类相关联的方法,而不是与从该类创建的任何对象相关联的方法.该类的每个实例都共享该类的静态方法.Java 8还允许在接口中定义静态方法,它们可以协助默认方法.

实现包含静态方法的接口时,静态方法仍然是接口的一部分,而不是实现类的一部分.因此,您不能在方法前添加类名称.相反,您必须在方法前面加上接口名称

例:

interface X
{
   static void foo()
   {
      System.out.println("foo");
   }
}

class Y implements X
{
}

public class Z 
{
   public static void main(String[] args)
   {
      X.foo();
      // Y.foo(); // won't compile
   }
}
Run Code Online (Sandbox Code Playgroud)

Expression Y.foo()不会编译,因为它foo()是接口X的静态成员而不是类的静态成员Y.

  • 好吧......但如果问题是方法的模糊继承,那么问题就变成:"我们已经有了这个问题,使用默认方法.由于Java包含对默认继承冲突的解决方案的支持(请参阅Styl的答案评论),为什么是静态继承有什么不同吗?" (3认同)
  • 这就是OP所知道的.他问*为什么*它是这样指定的,与类的静态成员相反. (2认同)

Sty*_*tyl 5

接口中的静态方法如果被继承,可能会造成死亡的钻石。因此,与从可能实现多个包含同名静态方法的接口的具体类中调用静态方法的风险相比,从适当的接口调用静态方法已经足够好了。

为什么静态方法有什么不同?

静态方法只是与对象无关的函数。我们没有将它们放在实用程序抽象类中(比如调用 Collections.sort() ),而是将这些函数(静态方法)移动到它们适当的接口中。它们可以像默认方法一样绑定到继承的对象,但这不是它们的工作。静态方法提供与类的实例无关的功能。

例子:

interface Floatable {

    default void float() {
        // implementation
    }

    static boolean checkIfItCanFloat(Object fl) {
         // some physics here
    } 
}

class Duck implements Floatable { }
Run Code Online (Sandbox Code Playgroud)

所以,关键是鸭子可以漂浮,但检查对象是否真的漂浮的函数不是鸭子可以做的。这是一个无关紧要的功能,我们可以将其传递给我们的 Floatable 接口,而不是让它位于某个实用程序类中。