为什么可以';Java编译器不能正确推断继承吗?
答案归结为为什么可以';Java编译器不能正确推断继承吗?,java,generics,Java,Generics,答案归结为Java不支持参数化方法的下限,因为这样的特性“不够有用”,请参阅 给出以下代码段: package demo; public class Demo { interface Foo { void foo(); } interface Bar { void bar(); } interface FooBar { <R extends Foo & Bar> R foobar(); static FooBar creat
Java不支持参数化方法的下限,因为这样的特性“不够有用”
,请参阅
给出以下代码段:
package demo;
public class Demo {
interface Foo { void foo(); }
interface Bar { void bar(); }
interface FooBar {
<R extends Foo & Bar> R foobar();
static FooBar create() { return new TypicalJavaFooBar(); }
}
private static final class TypicalJavaFooBar implements Foo, Bar, FooBar {
public void bar() { System.out.println("foo"); }
public void foo() { System.out.println("bar"); }
public <R extends Foo & Bar> R foobar() {
return (R) this;
}
}
public static void main(String[] args) {
FooBar x = FooBar.create();
Foo foo = x.foobar();
Bar bar = x.foobar();
x.foobar().foo();
x.foobar().bar();
}
}
软件包演示;
公开课演示{
接口Foo{void Foo();}
接口栏{void Bar();}
接口FooBar{
R foobar();
静态FooBar create(){返回新的TypicalJavaFooBar();}
}
私有静态最终类TypicalJavaFooBar实现了Foo、Bar、FooBar{
public void bar(){System.out.println(“foo”);}
public void foo(){System.out.println(“bar”);}
公共图书馆{
返回(R)本文件;
}
}
公共静态void main(字符串[]args){
FooBar x=FooBar.create();
Foo-Foo=x.foobar();
Bar=x.foobar();
x、 foobar().foo();
x、 foobar().bar();
}
}
如果没有在TypicalJavaFooBar#foobar
中显式转换为R
,编译器将失败,并出现以下错误
错误:(13,20)java:不兼容的类型:demo.demo.TypicalJavaFooBar无法转换为R
我的问题是为什么?在我看来,编译器应该有足够的信息,因为TypicalJavaFooBar
被明确定义为同时实现Foo
和Bar
;为什么这还不足以满足Foo&Bar
约束
更新
此练习的主要目标是定义以下契约:对
foobar
实例调用方法foobar
保证返回同时实现Foo
和Bar的内容。类型参数R
由调用代码绑定到方法,理论上可以是Baz实现Foo,Bar
;请参见,例如,Collections.emptySet()
,其类型参数由调用者确定,并且可能受类型见证的影响
要执行您显然正在尝试的操作,您需要将type参数移动到接口FooBar
,并让TypicalJavaFooBar实现Foo、Bar、FooBar
您的R
可以是任何类型的Foo&Bar
,因此您可以编写
class MyFooBar implements Foo, Bar { ...
FooBar x = new TypicalJavaFooBar();
MyFooBar mfb = x.foobar(); // this compiles now with the cast.
不,编译器无法推断R的类型,并且您的强制转换是错误的。
要了解原因,只需如下扩展您的类:
static class C extends TypicalJavaFooBar {
void eat() {}
@Override
public void bar() {}
@Override
public void foo() {}
}
然后,您的演员阵容允许:
FooBar x = new TypicalJavaFooBar();
C c = x.foobar();
c.eat();
这将导致运行时异常:
Exception in thread "main" java.lang.ClassCastException: demo.Demo$TypicalJavaFooBar cannot be cast to demo.Demo$C
这就是为什么——编译器的设计者肯定不允许这样做。代码中有错误,因此强制转换的必要性是。如果您可以在Java中使用它
if (obj instanceof (Foo & Bar))
那么你就不会被迫进行强制转换。@horatius这与这个问题无关。在TypicalJavaFooBar
中,你可以将foobar
方法的返回类型更改为justTypicalJavaFooBar
,这也会取消强制转换。虽然这不能回答你的问题,但在这种情况下我可能会这么做。虽然我通常更喜欢组合而不是继承,因此不太可能遇到这种情况。:-)@Andrey,不幸的是,您的“主要目标”在Java的类型系统中实际上是不可能的。您可以返回保证实现特定接口的内容,但不能表示“实现这两个接口的内容”。@Louis Wasserman,是的,您是对的,我刚刚尝试将“Foo&Bar”设为下限,但显然“不是有效的,可能是我更新了答案的副本,我只是想让演示简短一些。。。老实说,自从Java8问世以来,我发现非最终类、公共类或带有公共构造函数的最终类是一种可怕的做法。而且,你没有抓住重点。这里的要点是明确以下约定:在“foobar”实例上调用方法“foobar”保证返回同时实现“Foo”和“Bar”的内容。如果您发现非final的公共类非常糟糕,您将不会喜欢该语言的其他特性,就像cast在运行时显示ClassCastException风险的必要性一样。接口中的静态工厂方法=不处理公共类。此外,将Foo&Bar定义为一个下界也是可行的,即:“,我看不出不支持指定下界的有效原因…强制转换的必要性在于显示运行时java.lang.ClassCastException的风险。它可能是..IDK…但对我来说,if(obj instanceof(Foo&Bar))之间没有逻辑上的区别
和如果((obj instanceof Foo)和(obj instanceof Bar))
,但是第一个不编译