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

时间:2013-06-12 19:42:10

标签: java generics

我有几个提供的接口

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

5 个答案:

答案 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> getCopyRedStack<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)

您的类型SStack<T>的子类型,但复制方法是向上转播它到Stack<T>,可能是Stack<T>的任何子类型}。 您必须将复制结果强制转换为S