Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 参数化类型参数?_Java_Generics_Nested Generics - Fatal编程技术网

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) {...}
}

<>我可以添加一个类> p>我知道这可能不是你想听到的,但是即使java泛型看起来像C++模板一样,它们在工作方式上也有很大的不同。p> 在您喜爱的搜索引擎中查找
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
方法仍然采用type
Object
。当然,如果您传入一个对非
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) { ... }