我理解在Java中静态方法就像实例方法一样继承,不同之处在于重新声明它们时,父实现是隐藏的而不是被覆盖。好吧,这很有道理。但是,the Java tutorial注意到
接口中的静态方法永远不会被继承。
为什么呢?常规和接口静态方法之间有什么区别?
当我说静态方法可以继承时,让我澄清一下我的意思:
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.
}
然而,
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?)
}
答案 0 :(得分:17)
这是我的猜测。
由于Cat
只能扩展一个类,如果Cat
扩展 Animal
,那么Cat.identify
只有一个含义。 Cat
可以实现多个接口,每个接口都可以具有静态实现。因此,编译器不知道选择哪一个?
但是,正如作者所指出的那样,
Java已经存在此问题,使用默认方法。如果有两个接口 声明默认的void identify(),使用哪一个?这是一个编译 错误,你必须实现一个重写方法(可以 只是Animal.super.identify())。所以Java已经解决了这个问题 默认方法的问题 - 为什么不用于静态方法?
如果我再次猜测,我会在default
时说实施是Cat
vtable的一部分。使用static
它不可能。主要功能必须绑定到某些东西。在编译时,编译器可以用Cat.identify
替换Animal.identify
,但如果重新编译Cat
而不是包含main的类,则代码将不匹配现实。
答案 1 :(得分:14)
在 Java 8 之前,您无法在static
中定义interface
方法。这是经过深思熟虑的in this question。我将引用这个answer(由用户@ JamesA.Rosen)来说明为什么Java设计者最初可能不希望在static
中使用interface
方法:
这里有一些问题在起作用。首先是问题 声明静态方法而不定义它。这是不同的 之间
public interface Foo {
public static int bar();
}
和
public interface Foo {
public static int bar() {
...
}
}
Java也不允许,但它可以允许第二个。首先是 埃斯波提到的原因是不可能的:你不知道哪个 实现类是正确的定义。
Java可以允许后者,只要它将Interfaces视为 一流的物体。 Ruby的模块,大约是 相当于Java的接口,准确地说:
module Foo
def self.bar
...
end
end
但是,自 Java 8 发布以来,您实际上可以在default
内添加static
和interface
方法。
我将在这里引用这个source。这是最初的问题:
Java的界面语言功能允许您使用声明接口 抽象方法并提供这些方法的实现 实现接口的类。您需要实施 每种方法,当有很多方法时,这是繁琐的 实行。此外,在发布界面后,您无法添加新的界面 它没有破坏源和二进制的抽象方法 兼容性。
这是 Java 8 提供的解决方案default
:
Java 8通过改进支持接口来解决这些问题 默认和静态方法。默认方法是实例方法 在接口中定义,其方法标头以默认值开头 关键词;它还提供了一个代码体。每个实现的类 interface继承了接口的默认方法并可以覆盖 它们
对于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
}
}
表达式
Y.foo()
将无法编译,因为foo()
是静态成员 接口X
而不是类Y
的静态成员。
答案 2 :(得分:3)
接口中的静态方法如果被继承,就会产生死亡之钻。因此,与从可能实现包含相同名称的静态方法的多个接口的具体类调用它的风险相比,从适当的接口调用静态方法是足够好的。
为什么静态方法有所不同?
静态方法只是与对象无关的函数。我们将这些函数(静态方法)移动到适当的接口,而不是将它们放在实用程序抽象类中(比如调用Collections.sort())。它们可以像默认方法一样绑定到继承的对象,但这不是他们的工作。 静态方法提供与类实例无关的功能。
示例:
interface Floatable {
default void float() {
// implementation
}
static boolean checkIfItCanFloat(Object fl) {
// some physics here
}
}
class Duck implements Floatable { }
所以,重点是Duck可能会浮动,但是检查Object是否真正浮动的函数不是Duck可以做的事情。这是一个无关的函数,我们可以传递给我们的Floatable接口,而不是让它放在一些实用程序类中。
答案 3 :(得分:0)
让我们从一些背景开始...
Java不支持多重继承(扩展多个类的能力)。这是因为多重继承容易导致致命的死亡钻石(也称为钻石问题),Java的设计者选择抢先。
如果B和C覆盖了从A继承的方法,则D继承哪个方法?
一个类可以实现多个 interfaces ,这是因为接口方法是为覆盖而收缩的;如果类C实现了两个声明相同方法的接口A和B,则接口(A或B)的客户端将调用C中的相同方法。在模棱两可的情况下,通过强制实现者覆盖默认值,可以为Java 8中的接口引入默认方法。这是可以接受的折衷方案,因为默认方法旨在具有防御性(如果防御者未明确提供其他实现,则可以使用)。但是,由于编译器无法强迫您覆盖静态方法(固有方法不能覆盖静态方法),因此Java中为接口引入静态方法有一个限制:接口的静态方法是不继承。
答案 4 :(得分:0)
答案是静态方法属于一个类/接口,并且静态块只有一个实例。这就是您不能覆盖任何静态方法的原因。 即使在类中,您也可以重写,但仅执行超类的静态方法。