58 votes

Remplacement de méthodes privées en Java

Que succinctement décrit ici, en remplaçant les méthodes privées de Java n'est pas valide parce qu'un parent de la classe de méthodes privées sont "automatiquement finale, et caché de la classe dérivée". Ma question est en grande partie académique.

Comment est-il pas une violation de l'encapsulation de ne pas permettre à un parent privé de méthode pour être "remplacée" (c'est à dire, mis en œuvre de façon indépendante, avec la même signature, dans une classe enfant)? Un parent de la méthode privée ne peut pas être consulté ou hérités par un enfant de la classe, en cohérence avec les principes d'encapsulation. Il est caché.

Alors, pourquoi devriez-l'enfant de cette classe restreinte de mettre en œuvre sa propre méthode avec le même nom/signature? Est-il une bonne base théorique pour cela, ou est-ce juste une solution pragmatique de la sorte? Faire d'autres langages (C++ ou C#) ont des règles différentes en la matière?

70voto

Jon Skeet Points 692016

Vous ne pouvez pas remplacer une méthode privée, mais vous pouvez introduire un dans une classe dérivée sans problème. Cette compile fine:

class Base
{
   private void foo()
   {
   }
}

class Child extends Base
{
    private void foo()
    {
    }
}

Notez que si vous essayez d'appliquer l' @Override annotation Child.foo() vous obtiendrez une erreur de compilation. Aussi longtemps que vous avez votre compilateur/IDE ensemble pour vous donner des avertissements ou des erreurs si vous êtes en manque un @Override d'annotation, tout doit être bien. Certes je préfère le C# à l'approche de l' override être un mot-clé, mais il était évidemment trop tard pour le faire en Java.

Comme pour C#'s de la manipulation de "substitution" une méthode privée - une méthode privée ne peut pas être virtuel, en premier lieu, mais vous pouvez certainement introduire une nouvelle méthode avec le même nom qu'une méthode privée dans la classe de base.

27voto

Konrad Rudolph Points 231505

Ainsi, permettre à des méthodes privées à être remplacée va causer une fuite d'encapsulation ou d'un risque de sécurité. Si nous supposons que c'était possible, alors que nous avions en arriver à la situation suivante:

  1. Disons qu'il y a une méthode privée boolean hasCredentials() puis une classe étendue pourrait simplement remplacer comme ceci:

    boolean hasCredentials() { return true; }
    

    brisant ainsi le contrôle de sécurité.

  2. La seule façon pour la classe d'origine pour éviter cela serait de déclarer sa méthode final. Mais maintenant, c'est les fuites de mise en œuvre de l'information grâce à l'encapsulation, parce qu'une classe dérivée de maintenant ne peut pas créer une méthode hasCredentials plus: il serait en contradiction avec celle qui est définie dans la classe de base.

    C'est mauvais: permet de dire que cette méthode n'existe pas, au premier abord, Base. Maintenant, un réalisateur peut légitimement dériver une classe Derived et donner une méthode hasCredentials qui fonctionne comme prévu.

    Mais maintenant, une nouvelle version de l'original, Base classe est libéré. Son interface publique ne change pas (et n'ses invariants), donc on doit s'attendre à ce qu'il ne veut pas briser le code existant. Seulement, il n', parce que maintenant il y a un conflit de nom avec une méthode dans une classe dérivée.

Je pense que la question provient d'un malentendu:

Comment est-il /ne pas/ une violation de l'encapsulation de ne pas permettre à un parent privé de méthode pour être "remplacée" (c'est à dire, mis en œuvre de façon indépendante, avec la même signature, dans une classe enfant)

Le texte à l'intérieur des parenthèses est le contraire du texte avant elle. Java ne vous permettent de réaliser de façon indépendante [une méthode privée], avec la même signature, dans une classe enfant". Ne pas permettre cela violerait l'encapsulation, comme je l'ai expliqué ci-dessus.

Mais "pour ne pas permettre à un parent privé de méthode pour être "remplacée"" est quelque chose de différent, et nécessaires pour assurer l'encapsulation.

16voto

Gregory Pakosz Points 35546

"Faire d'autres langages (C++ ou C#) ont des règles différentes à ce sujet?"

Eh bien, C++ a des règles différentes: la statique ou dynamique de la fonction de membre de processus de liaison et les privilèges d'accès de l'exécution sont orthogonaux.

Donner une fonction de membre de l' private le privilège d'accès modificateur signifie que cette fonction ne peut être appelée que par sa classe de déclaration, pas par d'autres (même pas les classes dérivées). Lorsque vous déclarez une private fonction membre comme virtual, même virtuelle pure (virtual void foo() = 0;),- vous permettre à la classe de base pour profiter de la spécialisation tout en encore de faire respecter les privilèges d'accès.

Quand il s'agit de virtual des fonctions membres, des privilèges d'accès vous indique ce que vous êtes censé faire:

  • private virtual signifie que vous êtes autorisé à se spécialiser le comportement, mais l'invocation de la fonction membre est effectué par la classe de base, sûrement de manière contrôlée
  • protected virtual signifie que vous devriez / devez appeler la classe supérieure de la version de la fonction de membre lors de la substitution, il

Donc, en C++, le privilège d'accès et virtualness sont indépendants les uns des autres. Déterminer si la fonction est d'être statique ou dynamique liée est la dernière étape dans la résolution d'un appel de fonction.

Enfin, la Méthode de Modèle de modèle de conception doit être préférée à l' public virtual des fonctions de membre du.

Référence: Conversations: Virtuellement Vôtre

L'article donne une utilisation pratique d'un private virtual de la fonction membre.


ISO/IEC 14882-2003 §3.4.1

La recherche d'un nom peut associer plus d'une déclaration avec un nom si elle trouve le nom à un nom de fonction; les déclarations sont dit pour former un ensemble de fonctions surchargées (13.1). Résolution de surcharge (13.3) a lieu après la recherche du nom de a réussi. Les règles d'accès (clause 11) sont prises en compte seulement une fois que la recherche du nom et de la fonction de résolution de surcharge (le cas échéant) ont réussi. Seulement après la recherche d'un nom, de la fonction de résolution de surcharge (le cas échéant) et l'accès de la vérification ont réussi sont les attributs introduits par le nom de déclaration utilisés plus loin dans l'expression de traitement (article 5).

ISO/IEC 14882-2003 §5.2.2

La fonction est appelée dans un appel de fonction membre est normalement sélectionné selon le type statique de l'objet de l'expression (article 10), mais si cette fonction isvirtualand n'est pas spécifié à l'aide de aqualified-idthen la fonction appelée sera la finale de overrider (10.3) de la fonction sélectionnée dans le type dynamique de l'objet de l'expression [Note: le type dynamique est le type de l'objet pointé ou visées par la valeur courante de l'objet de l'expression.

7voto

Michael Borgwardt Points 181658

Un parent de la méthode privée ne peut pas être consulté ou hérités par un enfant de la classe, en ligne avec les principes d'encapsulation. Il est caché.

Donc, pourquoi est-ce que l'enfant doit être classe restreint à partir de la mise en œuvre de ses propres méthode avec le même nom/signature?

Il n'y a pas de telles restrictions. Vous pouvez le faire sans aucun problème, c'est juste ne s'appelle pas "primordial".

Méthodes de remplacement sont soumis à la répartition dynamique, c'est à dire la méthode qui s'appelle en réalité est sélectionné à l'exécution en fonction du type réel de l'objet, il est appelé. Avec la méthode privée, qui n'arrive pas (et ne devrait pas, selon votre première déclaration). Et c'est ce qui est signifié par la mention "privé méthodes ne peuvent pas être remplacé".

3voto

kdgregory Points 21849

Je pense que vous avez mal interprété ce que post dit. C'est pas en disant que l'enfant de la classe est "restreint de mettre en œuvre sa propre méthode avec le même nom/signature."

Voici le code, légèrement modifié:

public class PrivateOverride {
  private static Test monitor = new Test();

  private void f() {
    System.out.println("private f()");
  }

  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
    });
  }
}

class Derived extends PrivateOverride {
  public void f() {
    System.out.println("public f()");
  }
}

Et la citation:

Vous pourriez raisonnablement s'attendre à la sortie de la "public f( )",

La raison de cette citation est que la variable po détient une instance de la Dérivée. Cependant, puisque la méthode est définie comme privée, le compilateur fait regarde le type de la variable, plutôt que le type de l'objet. Et il se traduit par l'appel de méthode dans invokespecial (je pense que c'est le droit de l'opcode, vous n'avez pas vérifié JVM spec) plutôt que de invokeinstance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X