5

我有几个提供的接口

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>所以我在实现中也应该有两种泛型类型。

4

5 回答 5

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;
    }

}
于 2013-06-12T19:59:05.423 回答
2

由于您无法更改接口,因此您别无选择,只能进行暴力转换。

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

在 java 中,self 类型没有令人满意的答案。一个技巧是通过自引用类型参数,例如Foo<T extends Foo<T>>,但是它非常难看,并且它不能真正强制任何子类型Bar必须是 a 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(). 这个技巧很危险,调用者必须知道它在做什么。就我个人而言,我不会这样做;强制呼叫者进行演员表会更好。

于 2013-06-12T20:45:38.537 回答
1

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

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

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

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

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

public interface StackCopier<S extends Stack<T>, T extends Item> {

    S copy(S original);
}

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

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;
}
于 2013-06-12T22:24:04.840 回答
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;
    }
}

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

于 2013-06-12T19:48:34.780 回答
0

您的类型S是的子类型,Stack<T>但复制方法将其向上转换为Stack<T>可能是任何子类型的Stack<T>. 您必须将复制的结果转换为S

于 2013-06-12T19:53:27.967 回答