Je suis en train de créer une bibliothèque avec un conteneur qui libère des instances de ses objets contenus selon les descripteurs qui lui sont passés. J'aimerais que le descripteur détermine le type de l'objet retourné, mais le descripteur peut spécifier un type borné. Comment puis-je implémenter cela? Par exemple, le plus proche que je puisse obtenir est :
/*Bloc 1 - Première tentative. Compile, mais force l'utilisateur à effectuer une conversion forcée */
interface ItemDescriptor {
Class getType();
}
interface ArchiveContainer> {
Iterable getDescriptors();
I getItem(D descriptor);
}
//Implémentations
class ChannelItemDescriptor implements ItemDescriptor
{
final Class type;
ChannelItemDescriptor(Class type) {
this.type = type;
}
@Override Class getType() {return type;}
}
class ChannelArchive implements ArchiveContainer> {
@Override ByteChannel getItem(ChannelItemDescriptor descriptor) {...}
}
Le code ci-dessus compile, mais le problème est que la méthode getItem
de ChannelArchive
peut également retourner des SeekableByteChannel
. L'utilisateur de cette bibliothèque le sait au moment de la compilation (car il connaît le paramètre de type du descripteur), donc j'essaie d'éviter d'ajouter un paramètre de méthode de type Class
pour forcer l'utilisateur à convertir explicitement la valeur retournée en SeekableByteChannel
lorsque nécessaire. Je n'arrive pas à comprendre comment faire en sorte que getItem
renvoie un sous-type spécifique de ByteChannel
/*Bloc 2 - Code de test*/
ChannelArchive archive = ...;
ChannelItemDescriptor desc = ...;
ChannelItemDescriptor otherDesc = ...;
SeekableByteChannel sbc = archive.getItem(desc);
SeekableByteChannel sbc = archive.getItem(otherDesc); //Devrait échouer à la compilation, ou compiler avec un avertissement
ByteChannel bc = archive.getItem(otherDesc);
Je pourrais ajouter un paramètre Class
à chaque méthode, mais le code de la méthode ignorerait complètement le paramètre de méthode Class
! Son seul but serait d'aider le compilateur à inférer les types. Je pense que cela rendrait le code tellement obscur que ce serait plus facile pour l'utilisateur d'utiliser les vérifications instanceof
et les conversions.
J'ai essayé ceci :
/*Bloc 3 - Tentative échouée.*/
class ChannelArchive implements ArchiveContainer> {
// Ne compilera pas, getItem ne remplace pas
@Override II getItem(ChannelItemDescriptor descriptor) {...}
}
Mais cela ne fonctionne pas : ChannelArchive n'est pas abstrait et ne remplace pas la méthode abstraite getItem(ChannelItemDescriptor) dans ArchiveContainer
. Je suppose que c'est parce que le second paramètre de type a une éradication de type différente de ?
J'ai aussi essayé ceci, qui compile :
/*Bloc 4 - Presque assez spécifique*/
interface ArchiveContainer> {
Iterable getDescriptors();
> II getItem(DD descriptor);
}
class ChannelArchive implements ArchiveContainer> {
@Override > II getItem(DD descriptor) {...}
}
Même s'il compile, cela ne fonctionnerait pas vraiment parce que j'ai besoin d'un ChannelItemDescriptor
à l'intérieur de cette méthode, et la conversion résultante contredirait l'objectif de l'utilisation de la sûreté accrue des types génériques.
Je ne vois pas pourquoi je ne peux pas le faire, car les bons types sont connus au moment de la compilation. Ce dont j'ai vraiment besoin dans cette interface ArchiveContainer
est un paramètre de type paramétré, comme : >
. Qu'est-ce que je fais de mal?
REMARQUE : Je n'utilise pas réellement ByteChannel
et SeekableByteChannel
, mais ce que j'utilise est assez similaire.
À ruakh, j'ai opté pour le code du bloc 4. Dans mon cas, il est extrêmement peu probable que l'utilisateur envoie une mauvaise sous-classe d'ItemDescriptor
dans un appel à un getItem
, surtout parce que les descripteurs sont tous renvoyés par le conteneur lui-même via getDescriptors
!