Java 参数化类型参数?
我试图创建一个带有容器的库,该容器根据传递的描述符释放其包含对象的实例。我想让描述符决定返回对象的类型,但描述符可以指定一个有界类型。我如何实现这一点?例如,我能得到的最接近的结果是:Java 参数化类型参数?,java,generics,nested-generics,Java,Generics,Nested Generics,我试图创建一个带有容器的库,该容器根据传递的描述符释放其包含对象的实例。我想让描述符决定返回对象的类型,但描述符可以指定一个有界类型。我如何实现这一点?例如,我能得到的最接近的结果是: /*Block 1 - First Attempt. Compiles, but forces user to cast*/ interface ItemDescriptor<I> { Class<? extends I> getType(); } interface Arch
/*Block 1 - First Attempt. Compiles, but forces user to cast*/
interface ItemDescriptor<I> {
Class<? extends I> getType();
}
interface ArchiveContainer<I, D extends ItemDescriptor<? extends I>> {
Iterable<? extends D> getDescriptors();
I getItem(D descriptor);
}
//Implementations
class ChannelItemDescriptor<I extends ByteChannel> implements ItemDescriptor<I>
{
final Class<? extends I> type;
ChannelItemDescriptor(Class<I> type) {
this.type = type;
}
@Override Class<? extends I> getType() {return type;}
}
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
@Override ByteChannel getItem(ChannelItemDescriptor<? extends ByteChannel> descriptor) {...}
}
<>我可以添加一个
java类型擦除
。
不幸的是,仅仅因为一个类型在编译时是已知的,并不意味着该类型在运行时是可恢复的,甚至在以后的编译阶段也是可恢复的。我认为这段代码(几乎?)与您的第三次尝试相同,它与您将得到的代码一样好:
// in ArchiveContainer:
<II extends I, DD extends ItemDescriptor<II>> II getItem(DD descriptor);
// in ChannelArchive:
public <II extends ByteChannel, DD extends ItemDescriptor<II>>
II getItem(DD descriptor)
{ ... }
//在ArchiveContainer中:
II获取项目(DD描述符);
//在ChannelArchive中:
公开的
II获取项目(DD描述符)
{ ... }
泛型确实提供了一种使用两个单独的上界声明类型变量的方法:
public <T extends Foo & Bar> Foo fooBar(T t) { ... }
公共Foo fooBar(T T){…}
但显然,当其中一个上界是类型参数而不是类或接口时,这是不允许的:
类型变量有一个可选的界限,T&I1。。。在里面边界由一个类型变量,或一个类或接口类型T组成,后面可能跟有更多的接口类型I1,…,In。[…]如果任何类型I1。。。In是类类型或类型变量。[]
(重点是我的)。我不知道这是为什么
但我认为这不应该是个大问题。注意,即使将
Map
泛化为Map
,其get
方法仍然采用typeObject
。当然,如果您传入一个对非K
类型的对象的引用,该方法将始终返回null
(因为这样的对象不应该被插入到映射中),但这不会损害类型安全性。您的代码(即使是最上面的示例)格式非常不正确,这让你很难弄清楚你想做什么。例如,类ChannelItemDescriptor实现ItemDescriptor
是完全无效的语法;也许您的意思是类ChannelItemDescriptor实现ItemDescriptor
?同样地,class ChannelArchive[续]您的问题意味着您的顶级示例的唯一问题是ChannelArchive.getItem(…)
的返回类型比您希望的要宽。如果你可以发布一个只有这个问题的顶级示例的版本,那么帮助你就容易多了。顶级代码块就有这个问题。第二块演示了它。第三块和第四块都是失败的尝试,贴出来的目的是希望它们澄清我试图实现的目标。我的观点是,最上面的代码块有很多问题。你显然没有试图编译它。但是不管怎样,我想我现在明白了你想做的事情,并且已经发布了一个答案。我对C++模板没有任何经验,我知道类型擦除是为什么我不能在<代码> GeTimeT/<代码>中指定一个更具体的类型:<代码> ChhannCyrave(它擦除到同一个签名)。“不幸的是,仅仅因为一个类型在编译时是已知的,并不意味着该类型在运行时是可恢复的…”是的,我知道“…甚至在以后的编译阶段。”嗯,这对我来说是新的。什么构成了“后期编译阶段”?第二阶段是第三次尝试无法编译的阶段。但是,是的,我尝试了两个独立的上限,但困惑的是这不起作用。那将是完美的。第一次尝试是。。。满意。我只是想改进一下。在您给出的映射示例中,库代码处理强制转换和类型检查。在我的问题中,用户代码必须进行检查和强制转换。或者他们调用一个静态实用程序方法来实现它。我的例子更像是。。。使用参数化键类型调用get
,并期望值是值类型的特定子类。@MutantPlatypus:Er,我在本地有一个可以编译的版本。我认为这和我所说的“第二次尝试”是一样的(尽管我现在意识到你把它称为“第三次尝试”),但也许有一些不同?我将编辑我的答案,以包括实际编译的代码。[继续]像这样:interface-KeyType{}
interface-ValueType{}
class-foomap,我已经更新了我的问题来解释为什么这对我不起作用。:)谢谢你的帮助,这是个好主意,可能对其他人有用。但是我的问题是DD扩展了itemsdescriptor
DD
需要擦除到ChannelItemDescriptor
才能使用。否则我必须强制转换它,使泛型不是类型安全的。我认为Map这样做是为了保持与前泛型代码的兼容性,并且不能声明get(K键)
,因为这会擦除到get(Object键)
,导致名称冲突?@MutantPlatypus:但是get(K键)
会擦除到get(对象键
正是在不破坏兼容性的情况下更改get
方法的原因。(注意,put
已从put(对象键,对象值)
更改为put(K键,V值)
)它没有改变的原因是,能够查找不存在的对象的查找方法没有坏处:这些对象将不存在。
/*Block 4 - Almost specific enough*/
interface ArchiveContainer<I, D extends ItemDescriptor<? extends I>> {
Iterable<? extends D> getDescriptors();
<II extends I, DD extends ItemDescriptor<II>> II getItem(DD descriptor);
}
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
@Override <II extends ByteChannel, DD extends ItemDescriptor<II>> II getItem(DD descriptor) {...}
}
// in ArchiveContainer:
<II extends I, DD extends ItemDescriptor<II>> II getItem(DD descriptor);
// in ChannelArchive:
public <II extends ByteChannel, DD extends ItemDescriptor<II>>
II getItem(DD descriptor)
{ ... }
public <T extends Foo & Bar> Foo fooBar(T t) { ... }