A a; // de type "référence vers A"
I i; // de type "référence vers I"
J j; // de type "référence vers J"
B b; // de type "référence vers B"
C c; // de type "référence vers C"
c = new C(); // l'opérateur new renvoie une référence de type "référence vers C"
 // vers l'objet créé, de classe C
// la suite d'affectations suivante est valide
i = c; // C implémente I
j = c; // C implémente J
b = c; // B est ancêtre de C
i = b; // B implémente I
a = b; // A est ancêtre de B
a = c; // A est ancêtre de C
// chaque affectation ci-dessous déclenchera statiquement une erreur
j = b // <-- B n'implémente pas J
i = j // <-- J n'est pas une extension de I
b = a // <-- B n'est pas ancêtre de A
La même règle permet de convertir toute référence en une référence de type Object :
class A extends ... implements ... { ... } // extends Object implicite
Object o = new A();
Visibilité des champs et méthodes après transtypage, liaison dynamique.
Soit r une référence de type "référence vers X", pointant vers un certain objet de classe C. D'après les règles ci-dessus, X est donc soit une classe (concrète ou abstraite) ancêtre de C, soit une interface implémentée par C :
class A { 
 int x = 0:
 int y = 1;
class B extends A {
 int x = 2; // redéfinition du champ x
 int z = 3; // nouveau champ
 // le champ y est hérité
B b = new B();
A a = b;
// les expressions suivantes sont valides
... a.x... // la valeur est celle du champ a de A, soit 0
... b.x... // la valeur est celle du champ a de B, soit 2
... a.y... // valeur 1
... b.y... // y est hérité, valeur 1
... b.z... // valeur 3
// l'expression suivante est invalide
... a.z... // l'objet possède bien un champ z, mais il n'est 
 // pas visible dans A
class A { 
 void m() {
 System.out.println ("implémentation de m dans A");
class B extends A {
 // la méthode m est héritée
 // nouvelle méthode :
 void n() {
 System.out.println ("implémentation de n dans B");
class C extends B { 
 // la méthode n est héritée
 // redéfinition de m
 void m() {
 System.out.println ("implémentation de m dans C");
} 
A a = new A();
a.m(); // affiche "implémentation de m dans A"
B b = new B();
b.m(); // affiche "implémentation de m dans A" (héritage)
b.n(); // affiche "implémentation de n dans B"
a = b;
a.m(); // affiche "implémentation de m dans A"
a.n(); // <--- erreur : la méthode n n'est pas visible dans A
C c = new C();
c.m(); // affiche "implémentation de m dans C"
c.n(); // affiche "implémentation de n dans B" (héritage)
b = c;
b.m(); // affiche "implémentation de m dans C" (liaison dynamique)
b.n(); // affiche "implémentation de n dans B"
a = c;
a.m(); // affiche "implémentation de m dans C" (liaison dynamique)
a.n(); // <--- erreur : la méthode n n'est pas visible dans A
Cas des conversions vers String.
Toute expression peut être convertie implicitement (ou explicitement) dans le type "référence vers String". Dans le cas où cette expression n'est pas statiquement constante, il y a alors création dynamique d'un objet de classe String représentant la valeur de cette expression, et l'expression résultant devient une référence vers cet objet.
Transtypage explicite.
Le type d'une expression peut également être explicitement converti avec la syntaxe suivante :
 ("nouveau_type")"expression"
Où codice_3 est l'expression à convertir. S'il s'agit d'une expression composée, il faut l'encadrer par des parenthèses. La conversion explicite d'une expression doit être utilisée à chaque fois que l'on souhaite convertir une expression dans un type qui n'est pas plus haut dans la hiérarchie des types. Dans le cas des types numériques, cette conversion n'est sans pertes que si le type cible permet de représenter la même valeur. Dans le cas contraire, la valeur choisie dépend du type initial et du type cible. Dans le passage de float à int, la valeur choisie est par exemple la valeur entière de la valeur initiale :
int n;
float f;
n = 3;
f = n; // f vaut 3.0
f = f + 1; // conversion de 1 en 1.0 et somme : f vaut 4.0
n = (int) f; // n vaut 4
f = f + 1.5; // f passe à 5.5
n = (int) f; // 5.5 est arrondi en 5 : n vaut 5.
Pour les types de références, la conversion est libre : une référence de type quelconque peut être explicitement convertie en toute référence dont le type permet de manipuler l'objet référencé, selon les règles ci-dessus. La non-validité de cette conversion n'est en général pas détectable avant l'exécution :
class B extends A implements I { ... } 
Object o = new B(); // l'objet créé est de classe B
I i = (I) o; // valide : B implémente I
A a = (A) o; // valide : A est ancêtre de B
B b = (B) a; // valide
C c = (C) o; // invalide : C n'est pas ancêtre de B
Ces conversions "descendantes" sont bien sûr propices aux erreurs.
L'opérateur codice_4 permet de vérifier la validité d'une conversion avant de l'effectuer :
if (r instanceof C) {
 c = (C) r;
 // action sur les instances de C
else {
 // action sur les instances d'une autre classe
L'opérateur codice_4 et les conversions supportent également les tableaux (à partir d'un objet de type codice_6) :
Object r = getObject();
if (r instanceof int[]) {
 int[] valeurs = (int[]) r;
else {
Autoboxing.
Java 5 introduit un mécanisme permettant la simplification du transtypage, appelé "autoboxing". Ce mécanisme permet d'utiliser indifféremment les types primitifs et les classes wrappers. Exemple : 
Avant Java 5, il fallait écrire :
List integers = methodeRenvoyantDesIntegers();
for(int i = 0; i < integers.size(); i++) {
 Integer integer = (Integer)integers.get(i);
 int actuel = Integer.parseInt(integer);
 methodNecessitantUnInt(actuel);
Alors qu'avec Java 5, il n'est plus nécessaire de passer par codice_7 :
List integers = methodeRenvoyantDesIntegers();
for(int i = 0; i < integers.size(); i++) {
 int actuel = (Integer)integers.get(i);
 methodNecessitantUnInt(actuel);
On voit que les codice_8 et les codice_1 sont utilisés indifféremment.
Toutefois, il n'est pas possible de déclarer un type générique avec un type primitif.
Il faut utiliser la classe englobante correspondante.
Exemple :
ArrayList<Integer> counters = new ArrayList<Integer>();
counters.add(500);
int n = counters.get(0);
Les limites de l'autoboxing est qu'il ne concerne que chaque type primitif et sa classe englobante respective. Par exemple le code suivant renvoie l'erreur codice_10 en voulant passer du "String" au "Float" :
 public static void main(String[] args) {
 for(int i = 0; i < args.length; i++) {
 System.out.println((Float)args[i]);
Il faut donc écrire :
public static void main(String[] args) {
 for(int i = 0; i < integers.size(); i++) {
 System.out.println((Float.valueOf(args[i])).floatValue());

Langue des signes française/Dialogues simples