Pensez-y en termes d'interface plus facile.
Supposons que la bibliothèque de base dispose d'une interface pour Animal. Et une implémentation appelée Dog. C'est-à-dire que Dog implémente Animal.
Vous créez maintenant une nouvelle implémentation d'Animal appelée Human.
L'homme met également en œuvre l'animal.
Je peux donc maintenant le faire.
Animal a1, a2;
a1 = new Dog();
a2 = new Human();
Mais je ne peux PAS faire cela.
Human h = new Dog();
Certes, les humains et les chiens sont des animaux, mais un chien ne peut en aucun cas remplacer un humain. Il s'agit d'une exception à la compilation.
Le cas est maintenant un peu plus complexe.
List est l'interface principale, et l'implémentation de base est LinkedList. En d'autres termes, LinkedList implémente List.
En prolongeant mon analogie, vous avez donc créé une nouvelle classe appelée HumanList qui étend également List.
Donc, en répétant ce que j'ai fait précédemment, vous pouvez le faire.
List<String> b1, b2;
b1 = new LinkedList<String>;
b2 = new HumanList<String>;
Mais vous ne pouvez pas le faire.
HumanList<String> = new LinkedList<String>.
Bien sûr, HumanList et LinkedList sont toutes deux des implémentations de List. Mais une LinkedList ne peut pas remplacer une HumanList dans toutes les situations.
En effet, votre implémentation de HumanList peut, en plus de l'interface List, avoir d'autres méthodes telles que displaySocialSecurityNumbers().
Si votre HumanList n'a pas d'autres méthodes que List, et que vous n'en aurez jamais besoin, vous pouvez faire ce qui suit.
List<E> b2 = new HumanList<E>();
Enfin, comme l'a déjà suggéré Xgord, si vous voulez suggérer que votre HumanList est un type de LinkedList, alors vous devriez faire en sorte que HumanList étende LinkedList.