我正在尝试使用容器创建库,该容器根据传递的描述符释放其包含对象的实例。我想让描述符确定返回对象的类型,但描述符可以指定有界类型。我该如何实现?例如,我能得到的最接近的是:
/*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) {...}
}
上面的代码可以编译,但是问题是ChannelArchive
的 getItem
可以回SeekableByteChannel
也是。这个库的用户在编译时知道这一点(因为他们知道描述符的类型参数),所以我试图避免添加 Class
类型的方法参数用于强制用户将返回值显式转换为 SeekableByteChannel
必要时。我不知道如何获得 getItem
返回 ByteChannel
的特定子类型无需强制用户转换。我想这样做:
/*Block 2 - Test code*/
ChannelArchive archive = ...;
ChannelItemDescriptor<SeekableByteChannel> desc = ...;
ChannelItemDescriptor<ByteChannel> otherDesc = ...;
SeekableByteChannel sbc = archive.getItem(desc);
SeekableByteChannel sbc = archive.getItem(otherDesc); //Should fail to compile, or compile with warning
ByteChannel bc = archive.getItem(otherDesc);
我可以添加一个Class<? extends I>
每个方法的参数,但该方法的代码将完全忽略 Class
方法参数!它的唯一目的是帮助编译器推断类型。我认为它只是混淆了代码,以至于让用户使用 instanceof
会更容易。检查和转换。
我已经试过了:
/*Block 3 - Failed attempt.*/
class ChannelArchive implements ArchiveContainer<ByteChannel, ChannelItemDescriptor<? extends ByteChannel>> {
//Won't compile, getItem doesn't override
@Override <II extends ByteChannel> II getItem(ChannelItemDescriptor<II> descriptor) {...}
}
但这不起作用:ChannelArchive is not abstract and does not override abstract method getItem(ChannelItemDescriptor<? extends ByteChannel>) in ArchiveContainer
.我假设这是因为第二个类型参数 <II extends ByteChannel>
删除类型不同于 <? extends ByteChannel>
?
我也试过这个,编译:
/*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) {...}
}
即使它编译了,它也不会真的工作,因为我需要一个 ChannelItemDescriptor
在该方法中,生成的转换将破坏使用泛型的附加类型安全性的目的。
我不明白为什么我不能这样做,因为正确的类型在编译时是已知的。我真正需要的是 ArchiveContainer
接口(interface)是参数化类型参数,如:<II extends I, DD extends D<II>>
.我做错了什么?
注意:我实际上并没有使用 ByteChannel
和 SeekableByteChannel
, 但我使用的是非常相似的。
这就是 ruakh,我选择了 block 4 中的代码。在我的例子中,用户发送错误的 ItemDescriptor
子类的可能性很小。在调用 getItem
,特别是因为描述符都是从 ArchiveContainer
返回的本身通过 getDescriptors
!
最佳答案
我认为这段代码(几乎?)与您的第三次尝试相同,与您将获得的一样好:
// 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) { ... }
但显然,当上限之一是类型参数而不是类或接口(interface)时,这是不允许的:
Type variables have an optional bound, T & I1 ... In. The bound consists of either a type variable, or a class or interface type T possibly followed by further interface types I1 , ..., In. […] It is a compile-time error if any of the types I1 ... In is a class type or type variable. [link]
(强调我的)。我不知道这是为什么。
不过我觉得这应该不是什么大问题。请注意,即使在 Map
之后被泛化为 Map<K,V>
, 它的 get
方法仍然采用类型 Object
.当然,该方法将始终返回 null
如果你传入一个对非 K
类型的对象的引用(因为这样的对象永远不应该插入到 map 中),但这不会损害类型安全。
关于java - 参数化类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9387738/