为什么void在Java中不是协变的?
如果我有这个界面:为什么void在Java中不是协变的?,java,polymorphism,covariance,Java,Polymorphism,Covariance,如果我有这个界面: public interface Foo { void bar(); } 为什么我不能像这样实现它 public class FooImpl implements Foo { @Override public Object bar() { return new Object(); } } 似乎虚空与一切都是协变的。我错过什么了吗 编辑: 我应该更清楚,我在寻找设计的理由,而不是它无法编译的技术原因。使void对所有事物都协
public interface Foo {
void bar();
}
为什么我不能像这样实现它
public class FooImpl implements Foo {
@Override
public Object bar() {
return new Object();
}
}
似乎虚空与一切都是协变的。我错过什么了吗
编辑:
我应该更清楚,我在寻找设计的理由,而不是它无法编译的技术原因。使void对所有事物都协变是否会产生负面影响?
void
实际上意味着它不返回任何内容。您可能会对C/C++中的void*
感到困惑。如果将接口更改为
public interface Foo {
Object bar();
}
它可以正常工作,因为Java中的所有对象都继承Object
所以,你可以这样做:
public class FooImpl implements Foo {
@Override
public Object bar() {
return new SpecialObject();
}
}
SpecialObject obj = (SpecialObject) new FooImpl().bar();
协方差意味着如果你的方法
返回类型
是一个超类型对象
你可以在运行时返回子类型对象
,void不是java.lang.object的超类型
(或者任何与此相关的对象,除了NPE先生回答的对象本身)返回类型必须与重写的方法匹配
Foo foo = new FooImpl();
foo.bar();
除非所有实现都符合接口并返回相同的对象(或者根本不返回任何对象),否则我怎么知道它将返回什么?协变返回类型通常指可以被更窄的类型替换的类型。在
Java
中,void
和Object
或其他任何东西之间没有这种关系(超类型子类型)。void
只与void
协变,因为:
返回类型为R1的方法声明d1为
返回类型可替换为另一个返回类型为R2的方法d2,
当且仅当以下条件成立时:
- 如果R1为
,则R2为void
void
- 如果R1是基元类型,则R2与R1相同
- 如果R1是引用类型,则:
- R1是R2的子类型,或者R1可以转换为R2的子类型 通过未经检查的转换(§5.1.9),或
- R1=| R2|
如果允许这样做,就无法确定是否应该编译
Foo foo = ...
Object o = foo.bar(); // is it void or an Object ?
void
不是java文件系统的一部分
如果我们把它变成一个类型,它更像是一个空类型,所以它应该是其他类型的子类型,也就是说,它与其他类型相反
在您的示例中,超类规定不应返回任何内容,而子类返回某些内容,因此子类违反了超类的约定
对象
与其他类型是协变的,如果您需要的话。从技术上讲void
不能是协变的返回类型,因为调用者需要知道堆栈布局。
调用返回对象的函数将导致INVOKEVIRTUAL/INTERFACE之后堆栈顶部的对象引用Void
return类型在堆栈顶部没有留下任何内容,因此函数是二进制不兼容的
因此,JLS正确地说这是不可能的。JLS对此非常清楚,但也许有人可以提供一个技术原因,说明为什么它可以这样设计。我猜
void
可能是对象的超类型,没有定义的方法。至少可以让这个例子起作用。我不知道它是否值得增加复杂性。@atamanroman,因为void
明确表示没有任何意义;这是一份声明该方法没有返回值的合同。能够返回某些内容将违反该约定——这只是它的设计方式。@DaveNewton在Java类型系统的其余部分中,没有为返回设置上限的例子。你说的是一个反对协变回报类型的论点。“Object
明确地表示Object;它是一个契约,声明该方法必须返回一个对象,仅此而已”。所以我的问题仍然是——有没有一个很好的理由不考虑<代码>空洞每个类型的超类型(至少为了返回协方差的目的)?@ MatthalaCE怎么说“空洞明确地意味着什么”,这是什么意思,除了那个以外的其他东西?!你说的不是JLS说的,我说的是。但是是的,有一个很好的理由允许定义一个显式不返回任何内容的方法。如果void是一个超类型,您如何指示一个方法永远不会返回某些内容?当然它不会编译,因为您是通过Foo
访问它的。就像newfooimpl(){publicstringbar(){returnsuper.bar();}}即使返回此.bar()
也不会编译。