我正在刷新Java并阅读Generics。由于在我的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);
}
}
如果有的话,我思考的根本缺陷是什么?我应该采取什么方法?
答案 0 :(得分: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中使用Generics之前做的事情。你显然冒着不具备类型安全性的风险,所以想一想你的想法是否真的让设计更有意义,因为它可能不是因为你无论如何必须做instanceof来检查类型以便在有用中使用Object
方式(即访问其方法)。
如果另一方面,您只需将c
成员(以及Caller
的构造函数参数)更改为:
CalledInterface<String> c;
您的Caller
将与CalledInterface
进行互动而不是实施,同时仍然是类型安全的。因此,您仍然可以传递Called
的实例并将其设置为c
。
答案 1 :(得分:1)
编辑后:
// I can do this:
c.set(c.get());
不,你不能。它不会在c
为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);
}
答案 2 :(得分:0)
您的代码中存在一些小错误:
protected Called c;
public Caller (CalledInterface arg) {
c = arg;
}
您不能在此处指定arg
,因为CalledInterface
类型不是Called
的子类型(反之亦然)
此外,您应该在使用CalledInterface
时提供类型信息(允许将其保留,但仅用于遗留目的)。
现在你想知道的部分。对于类型Called
,编译器知道get()
返回String
,如果您对此不感兴趣,您当然可以始终使用Object
作为element
的类型1}}。编译器也知道set()
将String
作为参数,因此它需要您给出一个。{1}}。在泛型中基本上与在没有泛型的情况下使用Object
基本相同(即使在您使用它的位置上不允许它,因为它没有意义)。这意味着您将告诉编译器忘记第一行上的类型(调用get()
)并在下面的行中取消它。