将Generic类的子类分配给此类的超类

我有几个提供的接口

public interface Finder<S extends Stack,T extends Item> { public S find(S s, int a); } public interface Stack { Stack getCopy(); } 

和实现第一个的类:

 public class SimpleFinder<S extends Stack,T extends Item> implements Finder{ public S find(S s, int a){ S stack = ....; ... stack = s.getCopy(); \\Error: incompatible types \\ required: S \\ found: Stack ..... return stack; } } 

如果我不能改变任何接口,那么最好的做法是尽可能保持实现的通用性?

编辑其他一些我无法破解的代码实例化SimpleFinder所以我在实现中也应该有两个generics类型。

问题是显然Stack不是S extends Stack 。 Java是强类型的,不会让你做这样的事情。

您可以转换为Stack ,在这种情况下,您仍会收到有关未经检查的转换的警告。 这意味着此转换不安全

 public class SimpleFinder, T extends Item> implements Finder { @Override public S find(S s, int a) { Stack stack = s.getCopy(); return (S) stack; } } 

或者只是使用Stack代替S extends Stack ,这是我的建议:

 public class SimpleFinder implements Finder, T> { @Override public Stack find(Stack s, int a) { Stack stack = s.getCopy(); return stack; } } 

由于您无法更改界面,因此您别无选择,只能进行暴力投射。

在更一般的讨论中,我们需要的是“自我类型”,我们想说方法调用foo.bar()应该返回静态类型的foo 。 通常需要自我类型用于流畅的API,其中方法应该返回foo本身。 在您的情况下,您想要返回一个新对象。

在java中,自我类型没有令人满意的答案。 一个技巧是通过自引用类型参数,如Foo> ,但它非常难看,并且它不能真正强制任何子类型Bar必须是Foo 。 而这个伎俩对你的情况根本没有帮助。

另一个技巧可行

 public interface Stack { > X getCopy(); } 

在这里,呼叫者提供准确的返回类型。

  S stack = ....; ... stack = s.getCopy(); // compiles, because X is inferred to be S 

此技巧有助于简化呼叫站点。 但是, getCopy()强制转换仍然存在,隐藏在getCopy()实现中。 这个技巧很危险,调用者必须知道它在做什么。 我个人不会这样做; 强制调用者最好进行强制转换。

正如评论中所讨论的,您的设计需要getCopy方法返回“自我类型” – 也就是说, BlueStack实现应该从其getCopy返回BlueStack ,并且RedStack应该返回一个RedStack

不幸的是,没有办法在Java中表达“自我类型”。 正如zhong.j.yu 指出的那样 ,递归类型参数接近,例如:

 //not recommended! public interface Stack, T extends Item> { S getCopy(); } 

但正如zhong.j.yu提到的那样,这是不直观的,并且仍然无法阻止BlueStack “撒谎”并从其getCopy返回RedStack

相反,我建议重新设计。 尝试将Stack类型的复制责任与Stack类型本身分离。 例如:

 public interface StackCopier, T extends Item> { S copy(S original); } 

如果StackCopier实现需要访问其各自Stack的私有成员,请考虑将它们作为嵌套类,例如:

 class BlueStack implements Stack { ... static class Copier implements StackCopier, T> { @Override public BlueStack copy(BlueStack original) { ... } } 

当然,需要将SimpleFinder更改为具有StackCopier字段或将其作为find的新参数:

 private final StackCopier copier = ...; public S find(S stack, int a) { S stackCopy = copier.copy(stack); ... return stackCopy; } 

您的类型SStack的子类型,但复制方法将其向上转换为Stack ,它可以是Stack任何子类型。 您必须将复制结果强制转换为S

 public class SimpleFinder,T extends Item> implements Finder{ public S find(S s, int a){ Stack stack = ....; ... stack = s.getCopy(); ..... return (S) stack; } } 

应该管用。 请记住,堆栈必须是Stack而不是S以匹配getCopy()返回类型。 我希望S类型是好的,因为它扩展了Stack ,但实现它是我正在观察的行为。