5

我目前正在复习我的 Java 并阅读泛型。由于它们在我的 Java 课程中没有得到广泛的处理,所以我仍然很难理解它,所以在回答时请记住这一点。

首先,我很确定我想要做的事情是不可能的。但是,我想找出我的想法错在哪里,以及我应该如何实现我想要的。

我正在尝试做的是操作一个对象,该对象从另一个不了解实例化类型的类实现通用接口。因此,我有以下类:

public interface CalledInterface<E> {
    public E get() { ... }
    public set(E e) { ... }
}


public class Called implements CalledInterface<String> {
    ...
}

现在我想做的是:

public class Caller {
    protected CalledInterface<?> c;

    public Caller (CalledInterface<?> arg) {
        c = arg;
    }

    public void run(){
        // I can do this:
        c.set(c.get());

        // But I'd want to be able to do something like:
        <?> element = c.get();
        c.set(element);
    }
}

如果有的话,我的想法中的根本缺陷是什么?我应该采取什么方法?

4

3 回答 3

8

首先,请记住泛型是编译时的东西而不是运行时。

现在在Caller你定义的Called c. Called被定义为实现CalledInterface<String>,因此自动Called在编译时生成以下方法:

String get();
void set(String e); //i assume you wanted to return void

所以本质上这并没有什么意义:

<?> element = c.get();

该类Caller甚至不知道Called在内部使用泛型,因为它只Called处理字符串。

更新

根据您的评论,由于您不想直接Caller使用Called而是使用CalledInterface您要做的第一件事就是将类型更改为该类型c。在这种情况下,您不应该使用泛型,因为泛型的全部意义在于,同一个类用于具有不同类型的不同场景(再次在编译时确定),强制类型而无需重复代码。

如果我理解正确,您不想限制Caller使用 String,那么您要做的就是更改CalledInterface为不使用泛型,并将方法更改为:

Object get();
void set(Object o);

这就是我们在 Java 1.4 中的泛型之前的做法。您显然冒着没有类型安全的风险,因此请仔细考虑您想要的内容是否真正具有设计意义,因为它可能没有,因为无论如何您都必须执行 instanceof 来检查类型以Object以有用的方式使用(即访问其方法)。

另一方面,如果您只是将c成员(以及 的构造函数参数Caller)更改为:

CalledInterface<String> c;

Caller将与CalledInterface而不是实现进行交互,同时仍然是类型安全的。所以你仍然可以传递一个实例Called并将其设置为c.

于 2012-12-06T12:09:03.027 回答
1

编辑后:

    // I can do this:
    c.set(c.get());

不,你不能。它不会与cbeing一起编译CalledInterface<?>。(你试过了吗?)

为此,您可以使用“捕获助手”:

private static <T> void helper(CalledInterface<T> c) {
    c.set(c.get());
}
public void run(){
    helper(c);
}

这也解决了你的第二个问题:

private static <T> void helper(CalledInterface<T> c) {
    T element = c.get();
    c.set(element);
}
public void run(){
    helper(c);
}
于 2012-12-06T19:45:42.953 回答
0

您的代码中有一些小错误:

protected Called c;

public Caller (CalledInterface arg) {
    c = arg;
}

此处不允许指定arg,因为类型CalledInterface不是的子类型Called(反之亦然)此外,您应该在使用时提供类型信息CalledInterface(允许将其省略,但仅用于遗留目的)。

现在到你想知道的部分。对于 type Called,编译器知道get()返回 a String,如果您对此不感兴趣,当然可以始终Object使用element. 编译器也知道set()需要一个String作为参数,所以它要求你给一个。在泛型中使用本质上与在没有泛型的情况下使用相同Object(即使在您使用它的位置上不允许使用它,因为它没有意义)。这意味着您将告诉编译器忘记第一行(调用get())的类型并在下面的行中忘记它。

于 2012-12-06T12:16:54.707 回答