tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

local_cafe Troize local_cafe

Date de première publication : 2021/10/13

Pour ce troisième TP, vous allez découvrir ...

L'héritage

L'héritage, à l'origine, a permis d'éviter la duplication de code mais il y a maintenant une véritable sémantique. Pour voir ce que cela, vous allez écrire trois classes : Heritage, Mere et Fille dans le même fichier :


public class Heritage {
   public static void main(String[] argv) {
      System.out.println("Demonstration de l'heritage");
   }
}

class Mere {

}

class Fille {

}

Les classes ne sont PAS imbriquées.

Si une classe Mere est dotée d'une méthode m()


class Mere {
  public void m() {
     System.out.println("methode de mere");
  }
}

Une classe fille ...


class Fille extends Mere {
}

public class Heritage {
  public static void main(String[] argv) {
      Fille f = new Fille();
      f.m();
  }
}

Une classe hérite de tous les attributs publics et protégés sauf des constructeurs. Dans le cas présent, le constructeur n'a pas été écrit alors le compilateur nous a offert une version par défaut. Nous allons vérifier de suite que les constructeurs ne sont pas hérités :


class Mere {
  protected String nom;

  public void m() {
     System.out.println("Je suis ta mere ["+nom+"]");
  }
}

Cet attribut est directement accessible dans la classe fille.


class Fille extends Mere {
  public Fille() {
     System.out.println("construction de fille");
     nom = "fille";
  }
}

La construction des objets

On va maintenant vérifier que construire un objet de classe fille construit bien un objet de classe mère même si on ne fait rien ... Pour cela, il faut simplement ajouter un constructeur sans argument à la classe mère :


class Mere {
  protected String nom;

  public Mere() { 
    System.out.println("Construction de mere par defaut");
  }
}

Si vous le voulez, vous pouvez spécifier l'appel au constructeur sans argument avec l'appel à super(); en première ligne. Mais si vous ne le faites pas, c'est fait quand même.


public class Heritage {
  public static void main(String[] argv) {
      Fille f = new Fille("essai");
      f.m();
  }
}

Écrivez le constructeur de la classe fille qui ferait marcher le code précédent ... Comment appeler le bon constructeur de Mere et que se passe-t-il si le bon constructeur de Mere n'est pas appelé ?

Le polymorphisme

Le polymorphisme fort ou overriding permet de redéfinir une méthode dans une hiérarchie.

La détermination de la bonne méthode se fait à l'exécution pas à la compilation, c'est pour cela que l'on dit que la méthode est virtuelle !

Ce processus de détermination peut être assez long car il implique la création d'une structure de données appelée table des méthodes virtuelles et le parcours de la dite table pour trouver la bonne version de la méthode. Cependant la machine virtuelle java peut être redoutablement efficace.


class Fille extends Mere {
  public void m() {
     System.out.println("methode de fille");
  }
}

Qu'est-ce que donne maintenant le appel de f.m(); ?

Dans le cas présent, on a complètement changé le code de la méthode m() mais on peut faire appel au code de la classe mère comme on veut :-)


super.m();

Pour finir, une bonne pratique est de doter la méthode redéfinie d'une annotation. Cela permet de prévenir la personne qui lit le code et surtout le compilateur qui va vérifier que la méthode est bien une rédéfinition. Ce qui va engendrer une erreur de compilation avec le code suivant :


class Fille extends Mere {
  @Override
  public void M() {
     System.out.println("methode de fille"+);
  }
}

Toutes les méthodes en Java sont virtuelles par défaut et peuvent donc être redéfinies. Si vous ne voulez pas que ce soit le cas, il ne faut pas hésiter à les déclarer final.


class Mere {
  protected String nom;

  final public void m() {
     System.out.println("Je suis ta mere ["+nom+"]");
  }
}

Quand on utilise des méthodes finales, on tronque la recherche dans la table des méthodes virtuelles, donc elles sont plus rapides à trouver. Ceci dit, il existe des optimisations de la machine virtuelle qui sont encore plus redoutables que cela ... et de loin...

Encapsulation

Pour finir, j'aime bien garder les attributs de la classe mère private et non protected. Je suis un intégriste de l'encapsulation : seules les méthodes de la classe mère permettent d'accéder aux attributs spécifiques de la classe mère.

Mais vous pouvez faire comme vous le sentez si c'est vous qui décidez !

Les petits mots clés (qualifiers)

Comment distinguer un paramètre de méthode qui porte le même nom qu'un attribut classique ou de classe ?

public class Qualifier {
  String nom;

  Qualifier() {
    nom = "attribut";
  }

  public void collision(String nom) {
    System.out.println("** Attribut ou parametre ?");
    System.out.println(nom);
  }

  public static void main(String[] argv) {
    Qualifier q = new Qualifier();
    System.out.println(q.nom);
    q.collision("parametre");
  }
}

this.nom est la solution

Ultimes remarques

Comparaison d'objets

Toutes les classes java dérivent implicitement de la classe java.lang.Object. Ainsi il existe une méthode equals() qui permet de tester l'égalité de deux objets.

Les méthodes equals() et hashCode() doivent être cohérentes !

On vous propose de tester ces méthodes avec des instances de la classe String :


String s1 = "moi";
String s2 = "moi";
String s3 = new String("moi");
String s4 = new String("moi");
String s5 = s3;
String s6 = null;

System.out.println(s1==s2);
System.out.println(s1==s3);
System.out.println(s3==s4);
System.out.println(s2==s3);
System.out.println(s1==s6);
System.out.println(s3==s6);

System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
System.out.println(s5);
System.out.println(s6);

System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s1.equals(s3));
System.out.println(s1.equals(s6));
System.out.println(s6.equals(s1));

Les résultats de ce code sont conformes pour tous les objets sauf pour s1 et s2 qui ont un statut particulier.

Ces objets spécifiques ne sont pas stockés comme les autres car il y a un pool des chaines de caractères statiques (connues par le compilateur) qui optimise le stockage et les comparaisons.

JUnit

JUnit est une bibliothèque de tests unitaires. Nous utiliserons pour les TPs sa version 5 (Jupyter). En général, il s'agit d'un fichier jar à télécharger qu'il faut inclure à la compilation et à l'exécution (tests). Les environnements de développement cachent toutes ces étapes.

"Extension Pack for Java" supporte JUnit. L'extension sous VS Code (par Microsoft) est proposée et téléchargée automatiquement. Sous VS Codium, il faut télécharger manuellement l'extension (proposée par vscjava). Il doit être possible d'installer en ligne de commande celle de Microsoft.

Pour activer l'environnement Java, il est nécessaire d' "ouvrir un répertoire", d'ouvrir un fichier Java. Quand l'extension est active, il ne reste plus qu'à activer les Tests Unitaires dans l'onglet "éprouvette". Si vous ne voyez pas l'onglet "éprouvette" ou bien on ne vous propose pas d'activer les Test Junits, redémarrer l'éditeur.

Un code de test ressemble à cela :


// imports spécifiques à la version
// certaines annotations / méthodes aussi
// si l'editeur est correctement configuré, les imports seront proposés automatiquement
// import static org.junit.jupiter.api.Assertions.assertTrue;
// import org.junit.jupiter.api.Test;


class TestVoiture {
  @Test
  void test() {
    Voiture v = new Voiture("ISIMA63");
    assertTrue(v.getImmatriculation().equals("ISIMA63")));
  }
}

Essayer de mettre en oeuvre ce test sur la voiture du TP précédent.

shared, déplacer votre travail hors de ce répertoire.

Si vous voulez utiliser JUNIT en dehors de VS Code ou Codium, j'ai décrit les lignes nécessaires dans le TP SVG