关于Java:为什么类静态方法是继承的,而不是接口静态方法?

Why are class static methods inherited but not interface static methods?

我明白,Java静态方法就像实例方法一样继承,不同之处在于,当它们被重新声明时,父实现将被隐藏而不是重写。好吧,这是有道理的。但是,Java教程注意到

Static methods in interfaces are never inherited.

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

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

1
2
3
4
5
6
7
8
9
10
11
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.
}

然而,

1
2
3
4
5
6
7
8
9
10
11
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?)
}


这是我的猜测。

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

然而,正如作者所指出的,

Java already has this problem, with default methods. If two interfaces
declare default void identify(), which one is used? It's a compile
error, and you have to implement an overriding method (which could
just be Animal.super.identify()). So Java already resolves this
problem for default methods – why not for static methods?

如果我再猜一次,我会说使用default,实现是Catvtable的一部分。用static就不能了。主函数必须绑定到某个对象。在编译时,编译器可以用Animal.identify替换Cat.identify,但如果重新编译Cat,而不是包含main的类,代码将与实际情况不匹配。


在Java 8之前,您不能在EDCOX1 1中定义EDCOX1 0个方法。这在这个问题中有很多讨论。我将引用这个答案(由用户@ JAMES.ROSEN)来解释为什么Java设计者可能不希望EDCOX1的0个方法在EDCOX1的1个初始阶段:

There are a few issues at play here. The first is the issue of
declaring a static method without defining it. This is the difference
between

1
2
3
public interface Foo {
  public static int bar();
}

and

1
2
3
4
5
public interface Foo {
  public static int bar() {
    ...
  }
}

Java doesn't allow either, but it could allow the second. The first is
impossible for the reasons that Espo mentions: you don't know which
implementing class is the correct definition.

Java could allow the latter, as long as it treated Interfaces as
first-class Objects. Ruby's Modules, which are approximately
equivalent to Java's Interfaces, allow exactly that:

1
2
3
4
5
module Foo
  def self.bar
    ...
  end
end

但是,由于Java 8的发布,实际上可以在EDCOX1×1中添加EDCOX1、4和EDCX1 0个方法。

我会在这里引用很多信息来源。这是最初的问题:

Java's interface language feature lets you declare interfaces with
abstract methods and provide implementations of those methods in the
classes that implement the interfaces. You are required to implement
each method, which is burdensome when there are many methods to
implement. Also, after publishing the interface you cannot add new
abstract methods to it without breaking source and binary
compatibility.

这是Java 8提供EDCOX1 OR 4的解决方案:

Java 8 addresses these problems by evolving the interface to support
default and static methods. A default method is an instance method
defined in an interface whose method header begins with the default
keyword; it also provides a code body. Every class that implements the
interface inherits the interface's default methods and can override
them

对于static

A static method is a method that's associated with the class in which
it's defined, rather than with any object created from that class.
Every instance of the class shares the static methods of the class.
Java 8 also lets static methods be defined in interfaces where they
can assist default methods.

When you implement an interface that contains a static method, the
static method is still part of the interface and not part of the
implementing class. For this reason, you cannot prefix the method with
the class name. Instead, you must prefix the method with the interface
name

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
   }
}

Expression Y.foo() will not compile because foo() is a static member
of interface X and not a static member of class Y.


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

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

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

例子:

1
2
3
4
5
6
7
8
9
10
11
12
interface Floatable {

    default void float() {
        // implementation
    }

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

class Duck implements Floatable { }

所以,重点是一个duck可以浮动,但是检查对象是否真正浮动的函数不是duck可以做的。它是一个无关的功能,我们可以传递给我们的浮动接口,而不是让它位于某个实用程序类中。


让我们从一些背景开始……

Java不支持多重继承(扩展多个类的能力)。这是因为多重继承倾向于致命的死亡钻石(也被称为钻石问题),Java设计者选择抢占它。

enter image description here

如果B和C重写从A继承的方法,那么D继承哪个方法?

一个类可以实现多个接口,因为接口方法是为重写而约定的;如果一个类C实现两个声明同一方法的接口A和B,则接口(A或B)的客户端将调用C中的同一方法。在Java 8中引入接口的默认方法是通过强制实现者在模糊情况下重写默认值来实现的。这是一个可接受的折衷方案,因为默认方法是防御性的(在实现者没有明确提供其他实现的情况下使用)。然而,由于编译器不能强迫您重写静态方法(静态方法本身不能被重写),所以Java中接口的静态方法的引入受到一个限制:接口的静态方法不会被继承。