DutReseau "oui c'est vrai, je les remercie mais quand tu dis:
a2 est déclarée A et est de type B.
je comprends pas la différence entre le type et le fait qu'elle soit déclarée A. "
La différence est que a2 sera utilisé en tant que A, mais ce sont les méthodes de B qui seront invoquée à partir de cet objet. De par sa déclaration, la JVM sait que c'est un A. Ca peut être un B, puisque l'héritage présent le permet, comme ça pourrait être une autre classe étendant A (ou B). Partant du principe que, dans le cas d'incertitude, on se ramène aux certitudes, a2 sera utilisé en tant que A, car c'est la seule certitude que l'on a.
On pourrait forcer son utilisation en tant que B par cast. Cela donnerait a1.f((B) a2);, mais cette possibilité est à prendre avec des pincettes, les downcast étant un indicateur d'une mauvaise architecture la plupart du temps et souvent sources de ClassCastException.
En bref, il faut découpler l'utilisation de l'objet en tant que tel et en tant que paramètre.