Comparable Abstrat Class

Résolu/Fermé
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017 - 9 févr. 2016 à 23:58
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017 - 13 févr. 2016 à 19:21
Bonjour à tous,

Après plusieurs tentatives et des recherches diverses et variées je me résous à venir demander de l'aider.

J'ai récemment fais un projet Java pour m'entraîner, j'ai mis en place des tris avec les interfaces Comparable et Comparator. J'ai Override les méthode compareTo et compare pour pouvoir effectuer mes tris.

Aujourd'hui je voudrai refaire pareil à la différence que j'ai une classe mère abstraite. Je ne sais pas si ça vient de ça ou que j'ai régressé pendant la semaine mais impossible de faire le tri par rapport à un attribut de type double.

Ma classe Acteur abstraite où j'implémente Comparable, Eclipse ne me demande pas d'Override compareTo dans cette classe, je ne sais pas si je dois le faire ou si ce n'est pas utile du coup.

public abstract class Acteur implements Comparable<Acteur> {
	
	private String nom;
	private String prenom;
	private int age;
	
	public Acteur(String nom, String prenom, int age)
	{
		this.nom = nom;
		this.prenom = prenom;
		this.age = age;
	}
	
	abstract double getSalaire();


public class Joueur extends Acteur implements Comparable<Acteur> {
	
	String club;
	double euroMillions;
	
	Equipe e;
	
	public Joueur(String nom,String prenom, int age, double euro)
	{
		this(nom,prenom,age,"Knysna Syndrome Club",euro);
	}

	public String getClub() {
		return club;
	}
	

	@Override
	public double getSalaire() {
		// TODO Auto-generated method stub
		
		    double rand = Math.random() * (1 - 0);
		    rand = (rand * this.euroMillions)/100;
		    return rand;
	}

	@Override
	public String toString() {
		DecimalFormat df = new DecimalFormat("####.##");
		return ""+getPrenom()+" "+getNom()+" joue dans le club de "+club+" et son salaire est de "+df.format(getSalaire())+"\n";
	}

	@Override
	public int compareTo(Acteur a) {
		
		return Double.compare(this.getSalaire(), a.getSalaire());
	}


}


Jusque là rien de bien dur, le seul vrai problème serait sûrement ma méthode compareTo qui n'est pas bien codée.

Enfin j'ai créé une classe Equipe qui sert de container aux joueurs, c'est d'ailleurs dans cette classe que je souhaite effectuer le tri. Le tri par rapport à la méthode getSalaire().

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class Equipe implements Iterable<Joueur> {
	
	public static final int NOMBREJOUEURS = 11;
	
	private String club;
	
	List<Joueur> equipe;
	
	public String getClub() {
		return club;
	}

	public void setClub(String club) {
		this.club = club;
	}

	public Equipe(String c)
	{
		this.club = c;
		equipe = new ArrayList<Joueur>();
	}
	
	public void add(Joueur j) throws EquipePleineException, JoueurHorsClubEquipeException
	{
		
		 if(equipe.size() == NOMBREJOUEURS)
			throw new EquipePleineException(j);
		else if(j.getClub() != this.club)
		{
			throw new JoueurHorsClubEquipeException(j);
		}
		else
		{
			
			j.e = this;
			this.equipe.add(j);
		}
			
	}
	
	public void triSalaire()
	{
		Collections.sort(equipe);
	}


Le principe est de créer des joueurs puis de les rentrer dans un ArrayList modélisé par la classe Equipe et ensuite je voudrai trier les joueurs à l'intérieur de cette équipe par leur salaire grâce à la méthode triSalaire()


Voilà je ne sais pas si j'ai été assez précis, j'aimerai avoir une piste pour savoir quoi faire pour que ça marche et surtout comprendre mon erreur.

Si quelqu'un à une idée pour m'aider, je suis preneur ! Merci d'avance :D

3 réponses

KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
Modifié par KX le 10/02/2016 à 20:08
Bonjour,

Ton problème vient du fait que getSalaire renvoie des données random.
Donc quand l'algorithme de tri fait ses comparaisons successives il peut appeler plusieurs fois la méthode getSalaire pour le même joueur, mais ne va jamais tomber sur la même valeur. Et même s'il arrive à terminer le tri, de toute façon au moment où tu vas afficher ta liste de joueur, le toString va encore appeler la méthode getSalaire et obtenir des valeurs différentes... ça n'a pas de sens !

De plus, si tu veux implémenter Comparable<A> il faut mieux le faire dans A, parce que si tu le fais dans B extends A d'une part et dans C extends A d'autre part, tu risques d'avoir des méthodes compareTo incompatibles dans B et dans C.
Cela peut entraîner des problèmes car b.compareTo(c) calculé avec B et c.compareTo(b) calculé avec C pourraient ne pas être symétrique ce qui va faire échouer le tri.

Remarque : si Eclipse ne te demande pas explicitement d'implémenter compareTo dans Acteur c'est tout simplement parce que c'est une classe abstraite, mais ça ne t'empêche pas de le faire quand même...

public abstract class Acteur implements Comparable<Acteur> {

    abstract double getSalaire();

    @Override
    public final int compareTo(Acteur another) {
        if (another == null)
            return 1;
        return Double.compare(getSalaire(), another.getSalaire());
    }
}

Remarques :
  • if(j.getClub() != this.club)
    est faux, on ne doit pas comparer la valeur de deux objets avec == mais avec la méthode equals
  • public void triSalaire() { Collections.sort(equipe); }
    est une mauvaise manière de coder, soit tes joueurs sont toujours triés, soit jamais, dans ce cas c'est à la méthode qui fait le getJoueur() de les trier ensuite.
  • DecimalFormat df = new DecimalFormat("####.##"); return ""+getPrenom()+" "+getNom()
    utilises plutôt String.format, c'est plus propre (même si comme pour le tri, je doute que ce soit à la méthode toString de faire une mise en forme aussi poussée).

@Override
public String toString() {
    return String.format("%s %s joue dans le club de %s et son salaire est de %.02f",
            getPrenom(), getNom(), getClub(), getSalaire());
}
La confiance n'exclut pas le contrôle
0
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017
Modifié par Hurobaki le 10/02/2016 à 23:02
Salut KX ! Décidément tu m'es d'une grande aide en ce moment !

Pour ce qui est du tri ça marche en me focalisant sur la classe mère merci de m'avoir éclairé !

J'ai corrigé les erreurs, mais j'aimerai avoir un peu plus de renseignements sur la "bonne" manière de coder par rapport au tri.

Est-ce que tu pourrais m'indiquer la méthode que tu utiliserais ?

Merci d'avance !
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
11 févr. 2016 à 19:57
De la manière dont tu as fait pour obtenir une liste triée tu es obligé de faire deux appels de méthodes :

Equipe e;
...
e.triSalaire():
List<Joueur> joueurs = e.getJoueurs();

Problème : la valeur de l'équipe est corrompue par ce tri. Il est impossible de revenir en arrière, une fois que le tri est fait, la valeur d'origine (non triée) est perdue.
Ce qui signifie que faire deux fois triSalaire() ne sert à rien. Sauf si tu a ajouté un joueur entre temps, dans ce cas il faut encore faire un triSalaire avant de récupérer les valeurs.
Ça commence à faire beaucoup de cas à envisager et finalement sans aucune garantie que lorsque tu vas récupérer la liste celle-ci sera effectivement bien triée.

Donc deux cas :
1) on considère que le tri est indispensable pour la gestion de l'équipe (ce qui n'est probablement pas pertinent dans ton cas) et dans ce cas on s'arrange pour avoir toujours une liste triée sans avoir à explicitement appeler une méthode de tri.
2) on considère que le travail de la classe Equipe n'est pas de gérer ce tri, dans ce cas on renvoie la liste des joueurs sans traitement particulier, et ce sera à la classe appelante de trier ces données comme elle le souhaite sur la copie des données qu'elle aura récupérée.

Exemples :

package test1;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

class Player {

    private final String name;

    public Player(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public String toString() {
        return name;
    }
}

class Team {
    
    private final SortedSet<Player> players;
         
    public Team() {
        players = new TreeSet<>((p1, p2) -> p1.getName().compareTo(p2.getName()));
    }
    
    public void add(Player player) {
        players.add(player);
    }
    
    public SortedSet<Player> getPlayers() {
        return Collections.unmodifiableSortedSet(players);
    }
    
    public String toString() {
        return players.toString();
    }
}

public class Test {
    public static void main(String[] args) {
        Team team = new Team();
        team.add(new Player("xyz"));
        team.add(new Player("abc"));
        team.add(new Player("rst"));
        SortedSet<Player> players = team.getPlayers();
        System.out.println(team);    // [abc, rst, xyz]
        System.out.println(players); // [abc, rst, xyz]
        team.add(new Player("uvw"));
        System.out.println(team);    // [abc, rst, uvw, xyz]
        System.out.println(players); // [abc, rst, uvw, xyz]
        players.add(new Player("ijk")); // UnsupportedOperationException
    }
}

package test2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

class Player {

    private final String name;

    public Player(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public String toString() {
        return name;
    }
}

class Team {
    
    private final List<Player> players;
         
    public Team() {
        players = new ArrayList<>();
    }
    
    public void add(Player player) {
        players.add(player);
    }
    
    public List<Player> getPlayers() {
        return Collections.unmodifiableList(players);
    }
    
    public String toString() {
        return players.toString();
    }
}

public class Test {
    public static void main(String[] args) {
        Team team = new Team();
        team.add(new Player("xyz"));
        team.add(new Player("abc"));
        team.add(new Player("rst"));
        List<Player> players = team.getPlayers();
        System.out.println(team);    // [xyz, abc, rst]
        System.out.println(players); // [xyz, abc, rst]
        SortedSet<Player> sorted = new TreeSet<>((p1, p2) -> p1.getName().compareTo(p2.getName()));
        sorted.addAll(players);
        System.out.println(sorted);  // [abc, rst, xyz]
        team.add(new Player("uvw"));
        System.out.println(team);    // [xyz, abc, rst, uvw]
        System.out.println(players); // [xyz, abc, rst, uvw]
        System.out.println(sorted);  // [abc, rst, xyz]
    }
}
0
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017
11 févr. 2016 à 22:27
Bonsoir KX,

Merci pour ta réponse, d'après ce que je vois il faut créer une liste qui servira au triage et à être afficher sans pour autant modifier l'attribut de la classe.

Il est vrai que cela me paraît plus propre au niveau du code. J'ai juste un soucis, Collections.unmodfiableList est un méthode permettant de ne pas modifier la liste retourner c'est ça ?
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
12 févr. 2016 à 19:41
En effet, ce qui est retourné n'est pas modifiable (d'où le nom).
Cela garantie que seule la classe Team puisse modifier sa liste de Player, en aucun cas une liste interne à une classe ne doit pouvoir être modifiée part autre chose que les méthodes de cette classe. Ce qui ce serait possible si tu faisais :

public List<Player> getPlayers() {
    return players;
}

Dans ce cas, il est tout à fait possible de faire
team.getPlayers().add("123");
ce qui peut entraîner des erreurs assez importantes, par exemple on peut dépasser 11 joueurs par équipe...

Par contre, ce que renvoie unmodifiableList ce n'est pas une copie de la liste de Team à un instant donné (on pourrait le faire). C'est la même liste que celle de l'équipe mais avec des méthodes en moins. Ainsi, lorsque l'on modifie l'objet team, les modifications sont également visibles dans la liste players.

Dans mon main c'était ces tests là :

List<Player> players = team.getPlayers();
System.out.println(team);    // [xyz, abc, rst]
System.out.println(players); // [xyz, abc, rst]
team.add(new Player("uvw"));
System.out.println(team);    // [xyz, abc, rst, uvw]
System.out.println(players); // [xyz, abc, rst, uvw]

On ajoute un joueur à l'objet team et la modification est visible dans l'objet team ET dans l'objet players. Cela fonctionnait également avec la version triée :

SortedSet<Player> players = team.getPlayers();
System.out.println(team);    // [abc, rst, xyz]
System.out.println(players); // [abc, rst, xyz]
team.add(new Player("uvw"));
System.out.println(team);    // [abc, rst, uvw, xyz]
System.out.println(players); // [abc, rst, uvw, xyz]
0
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017
Modifié par Hurobaki le 13/02/2016 à 19:21
Bonsoir KX,

J'ai refais tout mon code en m'appuyant sur ce que tu m'as dis, ainsi mon attribut liste n'est plus modifiable mis à part par les méthodes add et remove de la Collection.

Il est vrai que c'est plus propre de conserver la liste d'origine et d'effectuer les modifications sur une liste "temporaire".

Je tenais à te remercier pour le temps que tu as passer à m'expliquer et les exemples que tu as pris la peine de rédiger.

Bonne soirée à toi !
0