给定任何接口I
,可以声明一个变量,该变量包含对实现C
的类I
的任何对象的引用:
I i = new C();
我想做类似的事情。给定两个接口,我想声明一个变量,该变量包含对实现两个接口的类的任何对象的引用:
interface Foo { void foo(); }
interface Bar { void bar(); }
class Humpty implements Foo, Bar {
public void foo() { System.out.println("Humpty.foo()"); }
public void bar() { System.out.println("Humpty.bar()"); }
}
class Dumpty implements Foo, Bar {
public void foo() { System.out.println("Dumpty.foo()"); }
public void bar() { System.out.println("Dumpty.bar()"); }
}
public class Program {
public static void main(String[] args) {
// I actually have no idea what the syntax should be.
Random random = new Random();
// Fix: I previously used <? extends Foo, Bar>, thanks Jon Skeet and vijucat
<? extends Foo & Bar> foobar;
if (random.nextBoolean())
foobar = new Humpty();
else
foobar = new Dumpty();
foobar.foo();
foobar.bar();
}
}
我已尝试过上述代码段,但<? extends Foo, Bar>
会导致编译错误。正确的语法应该是什么?我想知道其他静态类型的JVM语言是否可行:Scala,Kotlin,Ceylon等。
答案 0 :(得分:3)
如果你不介意压制臭名昭着的“未经检查的演员”警告,这是一个“解决方案”:
@SuppressWarnings("unchecked")
public class Program {
public static<FooBar extends Foo & Bar> void main(String[] args) {
// Note the explicit cast needed
FooBar foobar = (FooBar) new Humpty();
foobar.foo();
foobar.bar();
// Note the explicit cast needed
foobar = (FooBar) new Dumpty();
foobar.foo();
foobar.bar();
}
}
另见Jon Skeet的答案,该答案适用于优雅可能的情况。
老实说,如果没有这么多角落案件,这应该是可能的。 Java Generics的实现非常混乱,不理解它们可能表明你知道如何明智地分配你的时间,而不是你不是技术领域的专家! : - )
答案 1 :(得分:2)
给定两个接口,我想声明一个变量,该变量包含对实现两个接口的类的任何对象的引用
不幸的是你做不到。您可以对泛型方法中的参数执行此操作,如下所示:
public static <T extends Foo & Bar> void someMethod(T value) {
Foo x = value;
Bar y = value;
}
...或者你也可以为泛型类中的实例变量做同样的事情:
class Test<T extends Foo & Bar> {
private T value;
public Test(T value) {
this.value = value;
}
}
...但你不能声明一个只需要满足这两个约束的变量。
(注意这里有两个约束的语法 - 它是&
而不是逗号。)
答案 2 :(得分:1)
你可以直接施放物体。你可以这样做:
Humpty foobar = new Humpty();
((Foo)foobar).foo();
((Bar)foobar).bar();
答案 3 :(得分:0)
您可以创建接口层次结构,但这实际上仅适用于相关接口。如果Bar和Foo完全不相关,这将不起作用。
interface Foo { void foo(); }
interface Bar extends Foo { void bar(); } //implementations of Bar will need to implement both foo() and bar()
Humpty和Dumpty只需要实现Bar,然后就可以创建一个对象:
<? extends Foo> foobar = new Humpty();
答案 4 :(得分:0)
这不是很好,但它有效,它是安全的(modulo null
引用),并且它不需要抑制任何警告。 Jon Skeet实际上朝着正确的方向前进,但在中间停了下来:
/**
* Existentially quantify over things that implement Foo and Bar.
*
* FooBar = \exists (T extends Foo & Bar). T
*/
interface FooBar extends Foo, Bar {}
/**
* Universally quantify over things that implement Foo and Bar.
*
* \forall (T extends Foo & Bar). T --> WrappedFooBar<T>
* \forall (T extends Foo & Bar). WrappedFooBar<T> --> FooBar
*/
class WrappedFooBar<T extends Foo & Bar> implements FooBar {
private T wrapped;
public WrappedFooBar(T wrapped) { this.wrapped = wrapped; }
public void foo() { this.wrapped.foo(); }
public void bar() { this.wrapped.bar(); }
}
public class Program {
public static void main(String[] args) {
Random random = new Random();
FooBar foobar;
if (random.nextBoolean())
foobar = new WrappedFooBar(new Humpty());
else
foobar = new WrappedFooBar(new Dumpty());
foobar.foo();
foobar.bar();
}
}
主要的警告是,这个解决方案显然无法扩展:人们可能需要编写大量的包装器。