将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; }
您的类型S
是Stack
的子类型,但复制方法将其向上转换为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
,但实现它是我正在观察的行为。