|
Publicité | |||||||||||||||||||||||
|
|
#1 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Depuis une très longue période, le mot-clé const est marqué réservé par dans de nombreux éditeurs IDE Java, tels qu'Eclipse, qui le colorie comme un mot-clé réservé du langage même s'il en interdit l'emploi dans le même temps.
Le mot-clé const a un sens différent de final. Car si final appliqué à une variable ou variable membre protège une référence et interdit à son pointeur de désigner autre chose que cette variable, const est supposer protéger effectivement cette variable contre des modifications par, entre-autres, des fonctions membres modificatrices. C'est ainsi que dans ce code d'exemple, pourvu d'un mot-clé final, ce mot-clé ne protège pas le contenu de la variable sur laquelle il s'applique. Et par là, je trouve son usage d'un intérêt limité. Code :
/** Une personne. */ class Personne { /** Le nom de la personne. */ private String m_szNom; /** Construire une personne qui a un nom. */ public Personne(String szNom) {setNom(szNom);} /** Renvoyer le nom de la personne. */ public String getNom() {return(m_szNom);} /** Fixer le nom de la personne. */ public void setNom(String szNom) {m_szNom = szNom;}; } /** Un programme d'essai. */ public class Essai { /** Un monsieur 'final'. */ private final Personne monsieur = new Personne("Remulus"); /** Un essai qui montre que final ne protège pas le contenu du monsieur. */ public void unEssai() { System.out.println("Avant: " + monsieur.getNom()); monsieur.setNom("Rébus"); System.out.println("Après: " + monsieur.getNom()); } /** Lancement de l'essai. */ public static void main(String[] args) { new Essai().unEssai(); System.exit(0); } } C:\build\classes>java -cp . Essai Avant: Remulus Après: Rébus (en revanche, dans mon code source, je n'aurais pas pu écrire ceci: Code :
public void unEssai()
{
System.out.println("Avant: " + monsieur.getNom());
monsieur.setNom("Rébus");
System.out.println("Après: " + monsieur.getNom());
monsieur = new Personne("Charade");
}
Alors, bien entendu, il existe le pattern immutable. Mais il pose un problème: il n'offre pas de garantie. Je peux par principe vous présenter une classe nommée TotoImmutable, et malheureusement, soit par oubli, soit par erreur, elle ne l'est pas réellement... Seul un mot-clé const, je crois, permettrait d'être réellement rassuré. Qu'en est-il de sa venue? Est-elle prévue? Dernière modification par grunt2000 ; 24/04/2007 à 16h41. |
|
|
|
|
|
#2 |
|
Membre régulier
![]() Date d'inscription: avril 2007
Messages: 116
|
je crois qu'elle est prevue
|
|
|
|
|
|
#3 | |
![]() Date d'inscription: février 2007
Localisation: Saint Etienne
Messages: 2 431
|
Citation:
Cela dit, même si je ne suis pas sûr que le cas particulier des const soit très intéressant, j'aimerais, moi aussi, que le langage évolue vers une plus grande expressivité. Soit le pattern de immutable qui est, je pense, garanti dès l'instant que l'on en respecte les règles. Tu pourrais toujours me répondre que l'on peut toujours se tromper. Et que ce serait bien, que grâce à un mot clef le compilateur vérifie cette qualité, et tu aurais raison. En ce genre de cas je me fais une sorte de règle de nommage, et j'essaie de trouver un test à la mode JUnit. |
|
|
|
|
|
|
#4 |
![]() Date d'inscription: novembre 2006
Localisation: Haut-Rhin
Messages: 3 776
|
bof, y a "static" voir "static final" pour faire la même chose...
|
|
|
|
|
|
#5 |
![]() Date d'inscription: janvier 2005
Messages: 2 706
|
Après avoir lu ton post ce matin, j'y ai réfléchi en allant en cours, et pour moi ce concept de const n'est pas applicable.
En fait, 2 possibilités: - soit le const est un modifieur d'une variable, exactement comme final Code :
const UnObjet o;
Ainsi, tu peux faire: Code :
const UnObjet o = new UnObjet(); UnObjet o2 = o; o2.methodeQuiModifie(); Mais là cela pose des problèmes. Imagine tu as une méthode qui prend en paramètre une instance de type UnObjet. Déjà ce qui est passée est une copie de la référence, donc il faut que le compilateur sache que c'était une const. Mais admettons. Code :
public static void methode(UnObjet o) { o.methodeQuiModifie(); } Code :
const UnObjet o = new UnObjet(); methode(o); // ne marche pas, car o est const, et la méthode attend un non const Code :
public static void methode2(const UnObjet o) { o.uneMethode(); } Code :
UnObjet o = new UnObjet(); methode2(o); //ne marche pas, l'objet doit être constant Ceci est quand même très embêtant. De plus, si une variable est déclarée const, il serait impossible de créer un objet const sans l'affecter à une variable. Code :
new const UnObjet(); //pourquoi pas? pas terrible quand même... - soit const serait un modifieur de classe, pour faire par exemple: Code :
public const class UneClasse {...} On se pose alors la question, comment vérifier l'immutabilité à la compilation? - il faut vérifier récursivement que les attributs de type primitifs sont finaux, et que les attributs références sont soit finaux, soit constants. Pour les finaux, c'est facile. Pour les constants, on a vu dans le premier point que const ne devait pas s'appliquer à une variable, donc il faudrait que la classe de chacun des attributs soit const (immutable). Or, avec le pattern immutable, on peut avoir des composants mutables tout en garantissant une classe immutable. par exemple: Code :
public class Immutable { private Point point; public Immutable(int x, int y) { point = new Point(x,y); } public int getX() { return point.getX(); } public int getY() { return point.getY(); } } (en y réfléchissant, pourquoi pas) Voilà pourquoi je pense que ça ne serait finalement pas une avancée aussi intéressante qu'il n'y parait... Sans parler de la compatibilité ascendante, des classes qui sont immutables par le pattern, qui devraient être composées dans des classes "const" alors qu'elles ne sont elles-même pas const (bien qu'immutable). Pour prendre un peu de recul, si on introduit un mot-clé (du moins son utilisation, car il existe déjà sans être utilisé) pour un design-pattern, pourquoi ne pas introduire également d'autres mots-clés : singleton, listener... Code :
public singleton class MonSingleton { ... } |
|
|
|
|
|
#6 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
@gifftane: les choses sont exactement comme tu les dis. Le pattern immutable fonctionne très bien dès que l'on en respecte les règles. Mais toi recevant utilisant une classe "Immutable" dont tu ne sais rien de l'auteur ou du code, tu ne peux qu'espérer qu'elle est bonne.
Il existe un cas de figure, où sauf à me tromper lourdement (c'est possible!!!), l'emploi du const semble indispensable. Il s'agit des EJB Stateless Remote et Local. Lorsque j'ai décris ce point d'entrée de service dans un EJB Stateless: Code :
public void valider(Commande cmd) { .... } En revanche, si l'EJB a une interface Locale, l'appel est - sauf mauvaise interprétation de ma part - direct. Les n couches de marshalling et unmarshalling disparaissent, et l'on a affaire à un appel Java classique, normal, où le paramètre cmd est cette fois passé par référence. Et par là, s'il est modifié dans la méthode valider, il sera restitué modifié à l'appelant. En passant Remote à Local, nous provoquons un changement de comportement potentiel dans notre application, aux effets de bords inattendus. Mais comment envisager sur cent, deux cents, cinq cents points d'entrée de service que nous aurions écrits ceux succeptibles de nous gêner, en mode Local? En mettant les paramètres d'entrées des services const, pour déclarer qu'au sein des méthodes EJB Stateless nous entendons bien ne pas les modifier, et demander au compilateur de vérifier ce fait. @om: Le mot-clé const existe et fonctionne. Il est implémenté en C++, et peut-être dans d'autres langages. Cela dit, const impose l'usage de règles comme tu l'as pensé. Par exemple, un objet const ne peut être passé qu'au travers d'une fonction annoncée const (c'est à dire ne modifiant pas l'objet this auquel elle va s'appliquer), ainsi que quelques autres règles. |
|
|
|
|
|
#7 |
![]() Date d'inscription: février 2007
Localisation: Saint Etienne
Messages: 2 431
|
Même s'il n'y a rien pour garantir l'immutabilité (dont il resterait à établir la relation avec const, je ne sais pas exactement comment notre ami à l'origine du post voit les choses), tu peux jouer avec le mot clef final.
Si ce terme ne garanti pas la constance de ses composants, il garanti au moins la constance de lui même, ce qui est déjà bien ! Pour les composants tu peux aussi leur affecter un final, pour les composants des composants aussi, etc. Tu peux régler ainsi le coté immuable d'un objet, mais jamais garantir sur la totalité. Mais moi je trouve que c'est déjà bien et intéressant. |
|
|
|
|
|
#8 | |||
![]() Date d'inscription: avril 2002
Messages: 10 416
|
Salut,
Citation:
Citation:
Citation:
static permet de déclarer des attributs ou des méthodes de classes (au lieu d'instance). final permet soit d'empêcher de redéfinir une méthode, soit d'empêcher de modifier la valeur d'une variable. Mais pour un objet cela empêche seulement de modifier la valeur de la référence, et le contenu de l'objet reste modifiable via ses mutateurs... Le const du C++ est différent :
Ce mot-clef permet, couplé avec le passage de référence, d'éviter de multiples copies d'objets inutile... Dès le début Java a adopté une autre approche, en se basant sur les classes immuables... Enfin je ne suis pas sûr que l'intégration du mot clef const soit évidente :
a++ PS : avant de poster je viens de voir qu'il y a eu plusieurs autres posts : ®om
__________________
adiGuba (blog & tutoriels) | Rédacteur/Modérateur Java Blog : Que peut-on attendre des closures de Java 7 ? | Projet Coin : Les modifications du langage pour Java 7 Créer des boutons avec les CSS3 |
|||
|
|
|
|
#9 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Avec const, mon programme deviendrait ceci:
Code :
/** Une personne. */
class Personne
{
/** Le nom de la personne. */
private String m_szNom;
/** Construire une personne qui a un nom. */
public Personne(String szNom) {setNom(szNom);}
/** Renvoyer le nom de la personne. */
public String getNom() const {return(m_szNom);}
/** Fixer le nom de la personne. */
public void setNom(String szNom) {m_szNom = szNom;};
}
Code :
/** Un programme d'essai. */
public class Essai
{
/** Un monsieur 'const'. */
private const Personne monsieur = new Personne("Remulus");
/** Un essai. */
public void unEssai()
{
System.out.println("Avant: " + monsieur.getNom());
monsieur.setNom("Rébus");
System.out.println("Après: " + monsieur.getNom());
}
/** Lancement de l'essai. */
public static void main(String[] args)
{
new Essai().unEssai();
System.exit(0);
}
}
Intérêt? Cela évite de créer une classe immutable, qui oblige: - à créer une classe, PersonneImmutable, compagnone de Personne, et reprenant tous ses getters. - soit à procéder par copie d'objet à l'intérieur de cette classe immutable, mais alors on a une consommation de temps, de mémoire, et le risque - toujours - d'avoir fait... une mauvaise méthode clone ou d'avoir fait un new Personne() mais en oubliant de recopier la totalité des valeurs de ses membres qui seraient utiles. - soit de définir l'immutabilité de la Personne comme une interface Immutable interne à la classe Personne, et qui ne reprend que les getters. Mais alors on perd les éventuels héritages et implémentations de Personne lorsque l'on retourne une PersonneImmutable. A mes yeux, donc, const est nettement plus élégant. Plus propre. @adiGuba: Enfin je ne suis pas sûr que l'intégration du mot clef const soit évidente : Soit les classes l'API standard sont modifiées afin de l'utiliser, et dans ce cas on peut se retrouver avec un grand nombre d'incompatibilitée (que se passerait-il si je redéfinit une méthode const sans respecter son "contrat" ?). Je ne pense pas que la redéfinition d'une méthode permettrait d'oublier son mot-clé. Il suffit d'écrire le compilateur et la JVM d'une manière qui convient. Soit les classes de l'API ne l'utilisent pas, et dans ce cas il perdrait beaucoup de son intérêt... Il faut effectivement repasser sur toutes les APIs, mais en limitant cependant son action aux prototypes des fonctions dans la majorité des cas. C'est un travail certainement fastidieux pour Sun, mais à tout prendre, je pense, moins que celui d'avoir mis en place les génériques. Dernière modification par grunt2000 ; 25/04/2007 à 11h16. |
|
|
|
|
|
#10 | |
![]() Date d'inscription: janvier 2005
Messages: 2 706
|
Citation:
il faut faire 2 méthode: print(String) et print(const String)... ça double pas mal de méthodes... Et une méthode qui renvoie un String, il faudrait définir si ce que ça renvoie c'est un const ou pas: Code :
public const String a(); Code :
public String b();
__________________
®om's blog | GnuPG : chiffrer et signer sous Ubuntu pour les nuls | Ma clé GPG http://rom.developpez.com Hébergez vos mails vous-même sur Ubuntu Server ! Dernière modification par ®om ; 25/04/2007 à 11h54. |
|
|
|
|
|
|
#11 | |
![]() Date d'inscription: février 2007
Localisation: Saint Etienne
Messages: 2 431
|
Citation:
(je commente les points importants dans le commentaire, soyez compréhensifs). Code :
/** Une personne. */ class Personne { /** Le nom de la personne. */ // -> // ici ajout de final, MAIS PAS DE STATIC ! // private final String m_szNom; /** Construire une personne qui a un nom. */ public Personne(String szNom) { //-> // remplacer : // // setNom(szNom); // par : m_szNom = szNom; // le compilateur contrôle que l'on initialise // dans le constructeur toutes les variables "final". } /** Renvoyer le nom de la personne. */ // -> // ceci reste permis. // public String getNom() {return(m_szNom);} /** Fixer le nom de la personne. */ public void setNom(String szNom) { // -> // Ceci devient interdit par le compilateur. // m_szNom = szNom;}; } Mais on a eu peur.
|
|
|
|
|
|
|
#12 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Oui, mais comment pourrais-tu alors utiliser deux instances de ta personne, l'une modifiable et l'autre non?
Car ce que je veux me permettre de faire, c'est ceci, à partir d'une unique définition de classe. Code :
Personne p1 = new Personne("Rébus"); const Personne p2 = new Personne("Charade"); Code :
p1.setNom("Devinette"); mais Code :
p2.setNom("Plaisantin"); Dernière modification par Ricky81 ; 05/05/2007 à 15h33. |
|
|
|
|
|
#13 | |||
![]() Date d'inscription: avril 2002
Messages: 10 416
|
Citation:
Il s'agit juste d'une approche différente du même problème (mais je ne dis pas qu'une est meilleure que l'autre). Citation:
Logiquement le compilateur interdit d'appeler des méthodes non-const depuis des méthodes const... Prenons l'exemple de HashMap. On est d'accord qu'un grand nombre de ses méthodes devraient être const (isEmpty(), size(), etc...), alors que d'autres ne le devraient pas (add(), remove(), etc...). Or, afin de gérer un cache, j'ai justement une classe qui hérite de HashMap et qui revérifie la validité de la Map avant chaque action. Donc le méthode isEmpty() peut aboutir à une modification de ma Map... et donc elle casserait le principe du const et donc mon code ne compilerait plus... Citation:
Mais surtout les Generics conservent la compatibilité a++
__________________
adiGuba (blog & tutoriels) | Rédacteur/Modérateur Java Blog : Que peut-on attendre des closures de Java 7 ? | Projet Coin : Les modifications du langage pour Java 7 Créer des boutons avec les CSS3 |
|||
|
|
|
|
#14 | |
![]() Date d'inscription: janvier 2005
Messages: 2 706
|
Citation:
Code :
Personne p1 = new Personne(); const Personne p2 = new Personne(); p1.setNom("Devinette"); //ok //p2.setNom("Plaisantin"); // ne compile pas p1 = p2; p1.setNom("Plaisantin"); //ok //p2 a été modifié... |
|
|
|
|
|
|
#15 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Non, ne t'inquiètes pas. L'affectation p1 = p2 est interdite également.
Tu ne peux pas affecter un objet constant à une référence se déclarant porteuse d'un objet non constant. |
|
|
|
|
|
#16 | |
![]() Date d'inscription: janvier 2005
Messages: 2 706
|
Citation:
Donc const Personne n'est pas compatible avec Personne. Donc si tu as une méthode, faisons simple: Code :
public static void afficher(Personne personne) {...} Code :
const Personne p = new Personne(); //afficher(personne); //interdit |
|
|
|
|
|
|
#17 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Non, l'inverse n'est pas vrai.
avec: const X a; X b; Il t'est 'interdit d'écrire: b = a; // Violation de la constance de a. mais pas: a = b; Tu peux affecter un objet non const à un pointeur const. Mais à travers ce pointeur const, tu ne pourras plus jamais le modifier. Ce lien parle du "const-correctness" dans les langages: http://en.wikipedia.org/wiki/Const. Voir la section final in Java en particulier. Ils ont l'air plus pessimistes sur son implémentation future. |
|
|
|
|
|
#18 | |
![]() Date d'inscription: janvier 2005
Messages: 2 706
|
Citation:
Le premier sens je l'ai montré dans le post précédent. Le second sens (il suffit d'inverser le sens d'affectation): Code :
Personne p1 = new Personne(); const Personne p2 = new Personne(); p1.setNom("Devinette"); //ok p2 = p1; //p2.setNom("Plaisantin"); // ne compile pas p1.setNom("Plaisantin"); //ok //p2 a été modifié... |
|
|
|
|
|
|
#19 |
|
Membre actif
![]() Date d'inscription: janvier 2007
Messages: 193
|
Oui, absolument!
Code :
Personne p1 = new Personne(); const Personne p2 = new Personne(); Il n'y a pas de magie dans ce cas-là: aucun moyen de l'éviter... En revanche, en manipulant p2 tu ne pourras rien modifier. C'est comme si je te donne un accès en lecture à mon compte en banque. Tu ne peux pas verser ou retirer de l'argent dessus. Mais si moi, dans le même temps, je conserve une autre référence dessus - elle modifiable - et que j'y mets ou que j'en retire de l'argent, eh bien nécessairement, tu verras les sommes évoluer dessus... alors que tu as un pointeur const! Mais l'important pour moi, c'aura été qu'en te donnant un accès const à mon compte en banque je serai sûr que toi tu ne pourras pas le modifier! const, ce n'est pas forcément la garantie qu'un objet garde la même valeur (ce serait trop complexe à vérifier), c'est garantir que l'on ne peut pas, soi, le modifier dans un certain périmètre. Dernière modification par Ricky81 ; 05/05/2007 à 15h34. |
|
|
|
|
|
#20 | |
![]() Date d'inscription: avril 2002
Messages: 10 416
|
Citation:
Et en même temps une simple interface peut amplement suffire pour "simuler" le const, par exemple dans ton cas : Code :
public interface ConstPersonne { public String getNom(); } Code :
/** Une personne. */ class Personne implements ConstPersonne { /** Le nom de la personne. */ // -> // ici ajout de final, MAIS PAS DE STATIC ! // private String m_szNom; /** Construire une personne qui a un nom. */ public Personne(String szNom) { // -> // remplacer : // // setNom(szNom); // par : m_szNom = szNom; // le compilateur contrôle que l'on initialise // dans le constructeur toutes les variables "final". } /** Renvoyer le nom de la personne. */ // -> // ceci reste permis. // public String getNom() { return (m_szNom); } /** Fixer le nom de la personne. */ public void setNom(String szNom) { // -> // Ceci devient interdit par le compilateur. // m_szNom = szNom; }; } Code :
public class Essai { /** Un monsieur 'const'. */ private ConstPersonne monsieur = new Personne("Remulus"); /** Un essai. */ public void unEssai() { System.out.println("Avant: " + monsieur.getNom()); monsieur.setNom("Rébus"); // ERREUR DE COMPILATION System.out.println("Après: " + monsieur.getNom()); } /** Lancement de l'essai. */ public static void main(String[] args) { new Essai().unEssai(); System.exit(0); } }
__________________
adiGuba (blog & tutoriels) | Rédacteur/Modérateur Java Blog : Que peut-on attendre des closures de Java 7 ? | Projet Coin : Les modifications du langage pour Java 7 Créer des boutons avec les CSS3 |
|
|
|
|
|
![]() |
||
L'apparition du mot-clé const est-il prévu dans une version à venir du JDK?
|
||
| Outils de la discussion | |
|
|