假设我有这个界面:
interface Foo<T extends Foo<T>> { // ... }
以下三个类实现它:
class TrueFoo implements Foo<TrueFoo > { // ... }
class FalseFoo implements Foo<FalseFoo> { // ... }
class WrongFoo implements Foo<FalseFoo> { // ... }
// ^^^^^^^^ this here is wrong, only
// Foo<WrongFoo> should be allowed
以下方法的方法签名需要强制我可以返回TrueFoo或FalseFoo类型的对象,而不是WrongFoo ...
public ???? getFoo(boolean which) {
return which ? new TrueFoo() : new FalseFoo();
}
以下不起作用:
这允许我也返回一个新的WrongFoo():
public Foo<?> getFoo(boolean which) {
return which ? new TrueFoo() : new FalseFoo();
}
这不允许我返回任何(以下说明):
public <FooType extends Foo<FooType>> FooType getFoo(boolean which) {
return which ? new TrueFoo() : new FalseFoo();
}
问题是FooType的类型由我调用函数getFoo()
的上下文决定,而不是由我实际尝试返回的对象决定。
在调用方法时执行此签名很简单,顺便说一下:
public <FooType extends Foo<FooType>> void test(FooType foo) {
// Do something with foo
}
如果您尝试使用WrongFoo实例调用它,则会出现绑定不匹配错误。
让我觉得应该有办法为方法返回类型执行此操作。
或者有没有办法在Foo的接口定义中强制执行此签名?
修改
为了帮助可视化这个界面应该做什么,你可以想一想这个版本:
interface CanCopy<Type extends CanCopy<Type>> {
public Type copy();
}
显然,像Foo implements CanCopy<Bar>
这样的课程没有任何意义,只有Foo implements CanCopy<Foo>
或Bar implements CanCopy<Bar>
。
编辑2:
正如解决方案存在的概念验证一样,我可以自己定义以下帮助程序类:
class FooResult {
private final Foo<?> foo;
public <FooType extends Foo<FooType>> FooResult(FooType foo) {
this.foo = foo;
}
@SuppressWarnings("unchecked")
public <FooType extends Foo<FooType>> FooType getFoo() {
return (FooType) foo;
}
}
然后我可以要求我的getFoo方法属于这种类型:
public FooResult getFoo(boolean which) {
return which ? new FooResult(new TrueFoo()) : new FooResult(new WrongFoo());
}
这样,我就无法从我的get方法中返回一个WrongFoo。
但是,显然,这有点过于复杂,不像Java Generics通常倾向于制作代码(根据我的经验)......所以,这可以以某种方式缩短吗?
编辑3:
我找到了另一种方法来为实现界面的人提供检查,方法是这样定义:
interface Foo<FooType extends Foo<FooType>> {
// Other interface definitions
/**
* Please implement with just this line in it:<br/><br/>
* <code>return this;</code>
* @return <code>this</code>
*/
public FooType returnThis();
}
现在,如果有人试图按上述方法实现WrongFoo类,他必须提供方法FalseFoo returnThis()
,如果他只是按照doctype中的要求将其实现为return this
,那么该行将抛出错误。
这不是一个保证,但它是一个非常好的假检查,可以防止因粗心复制类而导致的错误......而且如果需要带有此签名的消息,那将是一个很好的解决方案。
还有什么想法?
答案 0 :(得分:3)
您使用泛型的问题是症状,而不是问题:您要求您的界面依赖于您的具体实现。如果你想让你的getFoo接口方法返回一些不是WrongFoo的东西,你必须返回一些TrueFoo或FalseFoo而不是WrongFoo。您的类层次结构没有此特定间接。
要添加它,您需要执行以下操作:
class TrueFoo extends SpecialFoo {...}
class FalseFoo extends SpecialFoo {...}
class WrongFoo implements Foo<WrongFoo> {...}
class SpecialFoo implements Foo<SpecialFoo> {
SpecialFoo getFoo() {...}
}
请记住,Java支持协变返回类型,因此通过返回SpecialFoo,我们可以实现在Foo接口中创建的契约:Foo getFoo() {...}
如果您需要特定于TrueFoo和FalseFoo的行为,您可以进一步参数化SpecialFoo,依此类推,turtles all the way down。
修改强>
阅读完编辑后,我不相信你想要完成的是语言所支持的。看起来你想根据具体的实现来约束接口,根据定义,这将使它不是一个接口。我认为以下示例与您要找到的内容非常接近:
interface CanCopy<T extends CanCopy<T>> {
T copy();
}
interface FooCanCopy extends CanCopy<Foo> {
}
interface BarCanCopy extends CanCopy<Bar> {
}
class Foo implements FooCanCopy {
public Foo copy() {
return null;
}
}
class Bar implements BarCanCopy {
public Bar copy() {
return null;
}
}
我希望现在清楚为什么这种方法不起作用,即使在这里你也无法阻止某人做class Baz implements FooCanCopy
这样的事情。我更想知道你为什么要这样做?如果它保护开发人员不犯错误,可能还有其他选择,即内省单元测试或包装更改。