我有几个提供的接口
public interface Finder<S extends Stack<T>,T extends Item> {
public S find(S s, int a);
}
public interface Stack<T extends Item> {
Stack<T> getCopy();
}
和实现第一个的类:
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{
public S find(S s, int a){
S stack = ....;
...
stack = s.getCopy(); \\Error: incompatible types
\\ required: S
\\ found: Stack<T>
.....
return stack;
}
}
如果我不能更改任何接口,那么最好的做法是尽可能保持实现的通用性?
修改
我无法破解的其他一些代码实例化SimpleFinder<ClassA,ClassB>
所以我在实现中也应该有两个泛型类型。
答案 0 :(得分:3)
问题是显然Stack<T>
不是S extends Stack<T>
。 Java是强类型的,不会让你做这样的事情。
您可以转换为Stack<T>
,在这种情况下,您仍会收到有关未选中转化的警告。这意味着此转化不安全。
public class SimpleFinder<S extends Stack<T>, T extends Item> implements Finder<S, T> {
@Override
public S find(S s, int a) {
Stack<T> stack = s.getCopy();
return (S) stack;
}
}
或仅使用Stack<T>
代替S extends Stack<T>
,这是我的建议:
public class SimpleFinder<T extends Item> implements Finder<Stack<T>, T> {
@Override
public Stack<T> find(Stack<T> s, int a) {
Stack<T> stack = s.getCopy();
return stack;
}
}
答案 1 :(得分:2)
由于您无法更改界面,因此您别无选择,只能进行暴力投射。
在更一般的讨论中,我们需要的是“自我类型”,我们想说方法调用foo.bar()
应该返回静态类型foo
。通常需要自我类型用于流畅的API,其中方法应该返回foo
本身。在您的情况下,您想要返回一个新对象。
在java中,自我类型没有令人满意的答案。一个技巧是通过自引用类型参数,如Foo<T extends Foo<T>>
,但它非常难看,并且它无法真正强制任何子类型Bar
必须是Foo<Bar>
。而这个伎俩对你的情况根本没有帮助。
另一个技巧可能有效
public interface Stack<T extends Item> {
<X extends Stack<T>> X getCopy();
}
这里,调用者提供确切的返回类型。
S stack = ....;
...
stack = s.getCopy();
// compiles, because X is inferred to be S
此技巧有助于简化呼叫站点。然而,在getCopy()
的实现中隐藏了粗暴的强制转换。这个技巧很危险,调用者必须知道它在做什么。我个人不会这样做;强制调用者最好进行演员表。
答案 2 :(得分:1)
正如评论中所讨论的那样,您的设计需要getCopy
方法返回“自我类型” - 也就是说,BlueStack<T>
实现应该从其返回BlueStack<T>
getCopy
和RedStack<T>
应返回RedStack<T>
等。
不幸的是,没有办法在Java中表达“自我类型”。作为zhong.j.yu points out,递归类型参数接近,例如:
//not recommended!
public interface Stack<S extends Stack<S, T>, T extends Item> {
S getCopy();
}
但是,正如zhong.j.yu提到的那样,这是不直观的,仍然无法阻止BlueStack<T>
“撒谎”并从RedStack<T>
返回getCopy
。
相反,我建议重新设计。尝试将复制堆栈的责任与Stack
类型本身分离。例如:
public interface StackCopier<S extends Stack<T>, T extends Item> {
S copy(S original);
}
如果StackCopier
的实现需要访问各自Stack
的私有成员,请考虑将它们作为嵌套类,例如:
class BlueStack<T extends Item> implements Stack<T> {
...
static class Copier<T extends Item> implements StackCopier<BlueStack<T>, T> {
@Override
public BlueStack<T> copy(BlueStack<T> original) {
...
}
}
当然SimpleFinder
需要更改为StackCopier<S, T>
字段或将其作为find
的新参数:
private final StackCopier<S, T> copier = ...;
public S find(S stack, int a) {
S stackCopy = copier.copy(stack);
...
return stackCopy;
}
答案 3 :(得分:0)
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{
public S find(S s, int a){
Stack<T> stack = ....;
...
stack = s.getCopy();
.....
return (S) stack;
}
}
应该有效。请记住,堆栈必须是Stack<T>
而不是S
才能匹配getCopy()返回类型。我希望S
类型为Ok,因为它扩展了Stack<T>
,但实现它是我正在观察的行为。
答案 4 :(得分:0)
您的类型S
是Stack<T>
的子类型,但复制方法是向上转播它到Stack<T>
,可能是Stack<T>
的任何子类型}。
您必须将复制结果强制转换为S