渡された記述子に従って、含まれているオブジェクトのインスタンスを解放するコンテナを使用してライブラリを作成しようとしています。記述子が返されるオブジェクトの型を決定するようにしたいのですが、記述子は境界付きの型を指定できます。これを実装するにはどうすればよいですか?たとえば、私が得ることができる最も近いものは次のとおりです。
/*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
sgetItem
を返す可能性があること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
. これは、2 番目の型パラメーターの<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
のは、次のようなパラメーター化された型パラメーターです<II extends I, DD extends D<II>>
。私は何を間違っていますか?
注: 実際には and は使用しませんがByteChannel
、SeekableByteChannel
使用するものは非常に似ています。
ItemDescriptor
ブロック 4 のコードに落ち着きました。私の場合、ユーザーがa の呼び出しで間違っgetItem
たsublcass を送信する可能性はほとんどありませArchiveContainer
んgetDescriptors
。