为什么可以';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
方法的返回类型更改为just
TypicalJavaFooBar
,这也会取消强制转换。虽然这不能回答你的问题,但在这种情况下我可能会这么做。虽然我通常更喜欢组合而不是继承,因此不太可能遇到这种情况。:-)@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))
,但是第一个不编译