Envoyé par
tomlev
Pour ce qui est de l'accès aux membres privés ou protégés, les implémentations qui substituent la defender method peuvent le faire bien sûr, mais pas l'implémentation par défaut définie dans l'interface... Du coup, une defender method qui n'est pas substituée est fonctionnellement équivalente à une méthode d'extension ; c'est la possibilité de substitution qui fait finalement toute la différence.
Je ne suis pas du tout d'accord sur le fait que c'est juste la question de la substitution qui fait la différence, il y a une très grande différence aussi qui se manifeste rien qu'avec cet ajoute d’implémentation par défaut à l'interface, beaucoup de choses qu'on peut faire en java qu'on ne peut jamais faire en C#. Il me fallait juste le temps pour coder ces quelques linges de contre exemple:
Première grande différence est la suivante: prenons les deux exemples qui suivent en faisant la correspondance que tu penses qu'ils sont égales entre Java et C#:
On a notre classe MaClasse avec une seule méthode
1 2 3 4 5 6 7 8
| namespace app{
public class MaClasse{
public void affiche(){
Console.WriteLine("Affiche premier");
}
}
} |
On fait une extension avec la classe suivante :
1 2 3 4 5 6 7 8
| namespace app {
public static class ExtentionMaClasse{
public static void affiche2(this MaClasse m){
Console.WriteLine("Affiche deusieme ");
}
}
} |
Alors on donne d'abord le code des déclarations en Java, voici le code de MaClasse qui implémente une interface vide que je nome Affichable;
1 2 3 4 5
| public class MaClasse implements Affichable{
public void affiche(){
System.out.println("Affiche premier");
}
} |
Après je l’étend virtuellement en ajoutant une méthode par défaut affiche2() à Affichable, voici le code:
1 2 3 4 5 6
| public interface Affichable {
default void affiche2(){
System.out.println("Affiche deuxieme ");
}
} |
Alors à présent certes si je veux faire un appel classique de ces deux méthodes dans une classe Main le code sera identique que ça soit en Java ou C#:
1 2 3
| MaClasse objet = new MaClasse ();
objet.affiche();
objet.affiche2(); |
Résultat d'affichage pour les deux langages
Affiche premier
Affiche deuxieme
Mais si je veux faire comme ça en C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| namespace app
{
class MainClass
{
public static void Main(string[] args)
{
try {
MaClasse objet = new MaClasse ();
Type type = objet.GetType();
MethodInfo methode1 = type.GetMethod("affiche");
MethodInfo methode2 = type.GetMethod("affiche2");
//On a pas de paramètre pour nos méthodes
methode1.Invoke(objet,null);
methode2.Invoke(objet,null);//DEGAT DEGAT!!!
} catch (NullReferenceException e) {
Console.WriteLine("Ça ne va pas dutout"); }
}
}
} |
Et ben j'aurais bien ceci:
Affiche premier
Ça ne va pas dutout
Et pourtant en Java si je fais ça:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class MainClass {
public static void main(String[] args) {
try {
MaClasse objet = new MaClasse ();
Class<?> c=objet.getClass();
Method methode1 = c.getMethod("affiche",null);
Method methode2 = c.getMethod("affiche2",null);
//Il n y a pas de paramètre donc j'utilise la méthode qui n'exige pas de paramètre
methode1.invoke(objet);
methode2.invoke(objet);
} catch (IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
System.err.println("Ça ne va pas du tout");
}
}
} |
J'aurais bien ça:
Affiche premier
Affiche deuxieme
Ainsi, par réflexivité la réalité apparaisse, ce qui constitue une grande différence, je n'ai pas fait de substitution et voila le résultat correcte en Java. Par contre en C# j'obtiens juste ça
La 2e grande différence, c'est que quand je suis dans l'interface alors je suis bien dans un contexte qui représente toutes les classes et toute la descendance qui implémentent l'interface encours, ainsi à l'intérieur de la méthode par défaut je peux faire référence à n'importe quel type héritant déjà existant si je veux.
Pour cela, dans un contexte purement objet, en cas de besoin, j'accède à toutes les informations avec « this », pas besoin de passer quelque chose statiquement en paramètre d'une fonction.
Déjà, certes je ne peux pas accéder à la limite aux attributs privés sans substitution, mais avant tout les attributs privés ne sont destinés à être utilisés qu'à l'intérieur de la classe qui les définit.
Mais en plus de l'accès à n'importe quel méthode ou toute information disponible, je peux accéder avec « this » aussi aux attributs protégés d'une classe fille, selon le contexte d'attribut protégé définit par Java. Le plus important ici c'est que :« this » représente aussi une référence sur n’importe quel instance encours dont la classe fait partie de la descendance.
Pour cela je peux faire facilement ce qui suit, d'abord j'ajoute un attribut protégé à MaClasse que je nomme val:
1 2 3 4 5 6
| public class MaClasse implements Affichable{
protected String val="MaValeur";
public void affiche(){
System.out.println("Affiche premier");
}
} |
Et voila le code de ma méthode par défaut:
1 2 3 4 5 6 7 8
| public interface Affichable {
default void affiche2(){
if(this instanceof MaClasse){//Pour aller droit au but et éviter les bug qui n'ont pas de sens
//Je fais un cast sur this
System.out.println ("Affiche deuxieme avec valeur="+((MaClasse)this).val);
}
}
} |
Alors, ce bout de code compile en toute tranquillité et si je fais ceci:
1 2
| MaClasse objet = new MaClasse ();
objet.affiche2(); |
Ça m'affiche bien ceci:
Affiche deuxieme avec valeur=MaValeur
En conclusion, les deux concepts n'ont rien de commun que le fait que je les appels tous les deux méthodes et qu'ils se ressemblent syntaxiquement lors d'un appel classique.
Pour moi l'équivalent des méthodes d’extension en C# c'est tout simplement le surcharge d'opérateur en C++ un peu plus étendu qu'en C#, lorsque par exemple je surcharge une méthode static comme le flux de sortie de l'objet cout de type ostream, au lieu que je pusse juste faire seulement:
cout<<"chaine"<<"2echaine";
Je peux faire
cout<<objet<<"chaine"<<objet2<<...; //Et tout a été défini
C'est comme si on étend le type ostream avec une méthode dont la signature n'est autre qu'un surcharge de l’opérateur «<<» qui accepte en 2e paramètre un type autre qu'un pointeur sur char et qui retourne aussi la référence de l'objet de type ostream passé en 1e paramètre.
Ainsi, comme en C# l'opérateur «.» faisait partie de la liste d'exception des opérateurs non surchargeables, alors ils ont crée un mécanisme de le surcharger. Au lieu de faire ça maMethode(objet,params) pouvoir faire beau et faire objet.maMethode(params)
Déjà je ne pense pas qu'il fallait appeler ces méthodes des méthodes d’extension, on étend jamais une classe en la faisant passer en paramètre d'une méthode statique, il fallait juste dire des méthodes statiques surchargés syntaxiquement.
Partager