J'ai vu la discussion à cette question concernant la façon dont une classe qui implémente à partir d'une interface serait instanciée. Dans mon cas, j'écris un très petit programme en Java qui utilise une instance de TreeMap
, et selon l'opinion de chacun, il devrait être instancié comme:
Map<X> map = new TreeMap<X>();
Dans mon programme, j'appelle la fonction map.pollFirstEntry()
, qui n'est pas déclarée dans l' Map
interface (et quelques autres qui sont également présents dans l' Map
interface). J'ai réussi à le faire en lançant un TreeMap<X>
appel partout où j'appelle cette méthode comme:
someEntry = ((TreeMap<X>) map).pollFirstEntry();
Je comprends les avantages des directives d'initialisation décrites ci-dessus pour les grands programmes, mais pour un très petit programme où cet objet ne serait pas transmis à d'autres méthodes, je pense que ce n'est pas nécessaire. Pourtant, j'écris cet exemple de code dans le cadre d'une demande d'emploi, et je ne veux pas que mon code soit mal ni encombré. Quelle serait la solution la plus élégante?
EDIT: Je voudrais souligner que je suis plus intéressé par les bonnes bonnes pratiques de codage au lieu de l'application de la fonction spécifique TreeMap
. Comme certaines des réponses l'ont déjà souligné (et j'ai marqué comme étant la première à le faire), le niveau d'abstraction le plus élevé possible devrait être utilisé, sans perdre la fonctionnalité.
la source
Réponses:
"Programmer sur une interface" ne signifie pas "utiliser la version la plus abstraite possible". Dans ce cas, tout le monde utiliserait
Object
.Cela signifie que vous devez définir votre programme par rapport à l'abstraction la plus faible possible sans perdre de fonctionnalité . Si vous avez besoin d'un,
TreeMap
vous devrez définir un contrat à l'aide d'unTreeMap
.la source
TreeMap
comme exemple. «Programmer vers une interface» ne doit pas être considéré comme une interface ou une classe abstraite - une implémentation peut également être considérée comme une interface.public interface
plutôt que «les méthodes publiques d'une implémentation» .TreeMap
aurait surSortedMap
ouNavigableMap
Si vous souhaitez toujours utiliser une interface, vous pouvez utiliser
il n'est pas nécessaire de toujours utiliser une interface mais il y a souvent un point où vous voulez avoir une vue plus générale qui vous permet de remplacer l'implémentation (peut-être pour les tests) et c'est facile si toutes les références à l'objet sont abstraites comme type d'interface.
la source
Le point derrière le codage vers une interface plutôt qu'une implémentation est d'éviter la fuite des détails d'implémentation qui contraindraient autrement votre programme.
Considérez la situation où la version originale de votre code a utilisé un
HashMap
et l'a exposé.Cela signifie que toute modification apportée
getFoo()
est une rupture de l'API et rendrait les utilisateurs malheureux. Si tout ce que vous garantissez estfoo
une carte, vous devez le retourner à la place.Cela vous donne la flexibilité de changer la façon dont les choses fonctionnent dans votre code. Vous vous rendez compte que cela
foo
doit être une carte qui renvoie les choses dans un ordre particulier.Et cela ne casse rien pour le reste du code.
Vous pourrez par la suite renforcer le contrat donné par la classe sans rien casser non plus.
Cela plonge dans le principe de substitution de Liskov
Étant donné que NavigableMap est un sous-type de carte, cette substitution peut être effectuée sans modifier le programme.
L'exposition des types d'implémentation rend difficile la modification du fonctionnement interne du programme lorsqu'un changement doit être effectué. Ceci est un processus douloureux et crée de nombreuses solutions de contournement laides qui ne servent qu'à infliger plus de douleur au codeur plus tard (je vous regarde le codeur précédent qui a continué à mélanger les données entre un LinkedHashMap et un TreeMap pour une raison quelconque - croyez-moi, chaque fois que je voir votre nom en svn blâme je m'inquiète).
Vous souhaiterez toujours éviter les fuites de types d'implémentation. Par exemple, vous souhaiterez peut-être implémenter ConcurrentSkipListMap à la place en raison de certaines caractéristiques de performances ou vous aimerez simplement
java.util.concurrent.ConcurrentSkipListMap
plutôt quejava.util.TreeMap
dans les instructions d'importation ou autre chose.la source
En accord avec les autres réponses, vous devez utiliser la classe (ou l'interface) la plus générique dont vous avez réellement besoin de quelque chose, dans ce cas TreeMap (ou comme quelqu'un a suggéré NavigableMap). Mais je voudrais ajouter que c'est certainement mieux que de le lancer partout, ce qui serait en tout cas une odeur beaucoup plus grande. Voir /programming/4167304/why-should-casting-be-avoided pour certaines raisons.
la source
Il s'agit de communiquer votre intention sur la façon dont l'objet doit être utilisé. Par exemple, si votre méthode attend un
Map
objet avec un ordre d'itération prévisible :Ensuite, si vous devez absolument informer les appelants de la méthode ci-dessus, elle retourne également un
Map
objet avec un ordre d'itération prévisible , car il existe une telle attente pour une raison quelconque:Bien sûr, les appelants peuvent toujours traiter l'objet de retour en tant
Map
que tel, mais cela dépasse le cadre de votre méthode:Prendre du recul
Les conseils généraux de codage sur une interface sont (généralement) applicables, car généralement c'est l'interface qui fournit la garantie de ce que l'objet devrait être capable d'exécuter, alias le contrat . De nombreux débutants commencent par
HashMap<K, V> map = new HashMap<>()
et il est conseillé de le déclarer comme unMap
, car unHashMap
n'offre rien de plus que ce qu'unMap
est censé faire. À partir de cela, ils seront alors en mesure de comprendre (espérons-le) pourquoi leurs méthodes devraient intégrer unMap
au lieu d'unHashMap
, ce qui leur permet de réaliser la fonction d'héritage dans la POO.Pour citer une seule ligne de l'entrée Wikipedia du principe préféré de tout le monde lié à ce sujet:
En d'autres termes, l'utilisation d'une
Map
déclaration n'est pas parce qu'elle a un sens «syntaxiquement», mais plutôt que les invocations sur l'objet ne devraient se soucier que de son typeMap
.Code plus propre
Je trouve que cela me permet parfois d'écrire du code plus propre, surtout en ce qui concerne les tests unitaires. La création d'un
HashMap
avec une seule entrée de test en lecture seule prend plus d'une ligne (à l'exclusion de l'utilisation de l'initialisation à double accolade), quand je peux facilement le remplacer parCollections.singletonMap()
.la source