web-dev-qa-db-fra.com

Puis-je remplacer une méthode privée en Java?

Je sais que je peux utiliser la réflexion pour invoquer une méthode privée et pour obtenir ou définir la valeur d'une variable privée, mais je souhaite remplacer une méthode.

public class SuperClass {

    public void printInt() {
        System.out.println("I am " + getClass() + ". The int is " + getInt());
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {

    public static void main(String[] args) {
        (new SubClass()).printInt();
    }

    public int getInt() {
        return 2;
    }
}

Je veux que la méthode main dans SubClass imprime 2, mais elle imprime 1. J'ai entendu dire que cela pouvait se faire par la réflexion, mais je n'arrive pas à comprendre comment. Si ce n'est de la réflexion, est-ce que quelqu'un connaît une autre façon de le faire? (Mise à part la protection de SuperClass.getInt() ou la copie et le collage de la méthode printInt() dans SubClass.) Si le remplacement de la méthode privée est impossible, existe-t-il un moyen de lui appliquer une sorte de déclencheur qui invoquera une méthode dans ma sous-classe? soit avant ou après l'exécution de la méthode privée?

12
numbers longer

Les méthodes privées ne sont pas héritées et ne peuvent en aucun cas être remplacées. Celui qui vous a dit que vous pouviez le faire en réfléchissant mentait ou parlait de quelque chose d'autre.

Cependant, vous pouvez accéder à la méthode privée getInt de la sous-classe invoquée printInt comme suit:

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();
    System.out.println("I am " + clazz + ". The int is " +
                       clazz.getMethod("getInt").invoke(this) );
}

Cela aura pour effet que la méthode 'getInt de la sous-classe' sera appelée à partir de la __claire 'printInt. Bien sûr, cela échouera maintenant si la sous-classe n'a pas déclare une getInt, vous devez donc ajouter une vérification pour pouvoir gérer des sous-classes "normales" qui n'essayent pas de "surcharger" une méthode privée:

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();

    // Use superclass method by default
    Method theGetInt = SuperClass.class.getDeclaredMethod("getInt");

    // Look for a subclass method
    Class<?> classWithGetInt = clazz;
    OUTER: while( classWithGetInt != SuperClass.class ){

        for( Method method : classWithGetInt.getDeclaredMethods() )
            if( method.getName().equals("getInt") && method.getParameterTypes().length == 0 ){
                theGetInt = method;
                break OUTER;
            }

        // Check superclass if not found
        classWithGetInt = classWithGetInt.getSuperclass();
    }

    System.out.println("I am " + classWithGetInt + ". The int is " + theGetInt.invoke(this) );
}

Vous devez toujours changer le code de la superclasse pour que cela fonctionne, et comme vous devez changer le code de la superclasse, vous devez simplement changer le modificateur d'accès de getInt à protected au lieu de faire des piratages de réflexion.

14
trutheality

Vous ne pouvez pas remplacer une méthode privée, car aucune autre classe, y compris une classe dérivée, ne peut savoir qu'elle existe. C'est privé.

Les méthodes privées sont implicitement final.

Sur une note connexe, une sous-classe peut déclarer un champ ou une méthode avec le même nom qu'un champ ou une méthode private dans une super-classe, car du point de vue de la sous-classe, ces membres n'existent pas. Il n'y a pas de relation particulière entre ces membres.

17
erickson

Non, tu ne peux pas. Vous pouvez construire un programme dont look devrait pouvoir le faire, en utilisant le fait que le code d’une classe externe peut accéder aux membres privés de la classe imbriquée. Cependant, les méthodes privées ne peuvent toujours pas être remplacées. Exemple:

public class Test {

    public static class Superclass {
        private void foo() {
            System.out.println("Superclass.foo");
        }
    }

    public static class Subclass extends Superclass {
        private void foo() {
            System.out.println("Subclass.foo");
            // Looks like it shouldn't work, but does...
            super.foo();
        }
    }

    public static void main(String[] args) throws Exception {
        Superclass x = new Subclass();
        // Only calls Superclass.foo...
        x.foo();
    }
}

Étant donné qu'il s'agirait de la situation seulement / dans laquelle il était possible de remplacer une méthode privée, le fait qu'elle ne soit pas prise en charge n'est pas une grosse perte.

Si vous souhaitez modifier le comportement d'un membre privé de votre super-classe, votre conception est fondamentalement endommagée.

13
Jon Skeet

Je ne pouvais pas le faire avec réflexion, mais j'ai trouvé un moyen de le faire via l'instrumentation. J'ai utilisé ASM, comme suggéré sur Le blog de Dhruba Bandopadhyay . Si cela vous intéresse, vous pouvez consulter mon code (il y a trop de choses pour le poster ici).

5
numbers longer

Si vous voulez vraiment faire cela en utilisant la réflexion, vous pouvez:

public class SuperClass {

    private final Method getInt;

    public SuperClass() {
        /** Find the appropriate method to call and cache it. **/
        Method getInt = null;
        try {
            getInt = getClass().getDeclaredMethod("getInt");
        }
        catch (NoSuchMethodException e) {
            try {
                getInt = SuperClass.class.getDeclaredMethod("getInt");
            } catch (NoSuchMethodException e1) {
                throw new RuntimeException(e1);
            }
        }
        getInt.setAccessible(true);
        this.getInt = getInt;
    }

    public void print() {
        int val = 0;
        try {
            val = (Integer) getInt.invoke(this);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        System.out.println(val);
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {
    public int getInt() {
        return 2;
    }

    public static void main(String[] args) throws Exception {
        new SubClass().print();
    }
}

Je suppose que vous souhaitez concevoir votre programme différemment, plutôt que d'adopter cette approche. Ce n'est pas performant, et cela surprendra quiconque étend SuperClass par surprise.

Notez que la méthode getInt () de la sous-classe peut être privée si vous utilisez la réflexion de cette façon. Encore une fois, cette approche est presque certainement une mauvaise idée, pour au moins les deux raisons mentionnées. Les chances sont que vous pouvez atteindre vos objectifs d'une autre manière. La protection des colis n'est vraiment pas une option?

2
Michael Bosworth

Vous ne pouvez pas écraser une méthode privée, mais vous pouvez en introduire/redéfinir une dans une classe dérivée.

class Super
{
   private void fun()
   {
   }
}

class Child extends Super
{
    private void fun()
    {
    }
}

En cela, vous ne substituez pas ce fun () dans la classe Child, vous définissez une nouvelle méthode portant le même nom, mais si vous essayez d'utiliser @override au-dessus de fun () de la classe Child, vous obtiendrez une erreur de compilation, car vous forcez à écraser ce fun () sinon vous redéfinissez le nouveau fun () dans la classe Child

0
Moni

pouvez-vous passer var par référence -> classes utilisant Java?

parce que si c'est possible: le problème ci-dessus pourrait être réécrit comme suit:

(Notez que ceci est uniquement à des fins de spécification)

VB:

function (ByRef intX as Integer) // la valeur peut être changée explicitement, mais pas la méthode elle-même: P end function

tellement plus comme ça je suppose ...

0
i am ArbZ

Non, nous ne pouvons pas écraser les méthodes privées, mais il existe un cas dans lequel nous pouvons aussi écraser des méthodes privées, c.-à-d. Inner class

class PrivateTest { 

private String msg = "OuterAndInner";

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

class Inner extends PrivateTest {
    private void fun()  {
          System.out.println("Accessing Private Member of Outer: " + msg);
    }
}
public static void main(String args[])  {
     PrivateTest o = new PrivateTest();
     Inner  i   = o.new Inner();
     i.fun();
     // o.fun() calls Outer's fun (No run-time polymorphism).
     o = i; 
     o.fun();
}

}

0
Rahul Maurya

Vous ne pouvez pas remplacer une méthode privée. 

Une méthode de substitution ne peut exister que dans une sous-classe de la méthode de substitution. Si la méthode que vous souhaitez remplacer est marquée private , il n'est pas possible de la remplacer, car la sous-classe n'hérite d'aucun élément marqué private , coupant ainsi les liens entre cette méthode privée et tout élément de la sous-classe, y compris l'une des méthodes de la sous-classe.

"Remplacer" signifie permettre à la machine virtuelle Java de déterminer la méthode à utiliser en fonction du type d'instance avec lequel vous l'appelez. La théorie du polymorphisme explique ce qui rend cela possible. Pour que la machine virtuelle Java puisse dire "ces méthodes sont connectées, l’une remplace l’autre", la machine virtuelle Java doit pouvoir afficher la méthode remplacée méthode depuis la sous-classe qui contient la méthode substitution

Cela étant dit, les classes dans le cas que vous avez fourni seront toujours compilées, mais aucune des méthodes ne sera invoquée en fonction des machines virtuelles Java exécutées indépendamment par une invocation basée sur une instance. Il s'agira simplement de méthodes différentes qui ne seront pas reconnues par la JVM comme remplacées ou non.

De plus, si vous utilisez un IDE tel que NetBeans, l’annotation @Override indiquera au compilateur que vous substituez la méthode à la même signature dans la super-classe. Si vous ne le faites pas, vous recevrez un avertissement vous suggérant de créer l'annotation. Cependant, vous obtiendrez une erreur de compilation si vous appliquez cette annotation lorsque:

  • il n'y a pas de méthode avec une signature correspondante dans la superclasse

OR

  • la méthode dans la superclasse que vous essayez de remplacer est marquée private ou final

Reportez-vous à la documentation officielle d'Oraclehttps://docs.Oracle.com/javase/tutorial/Java/IandI/override.html Pour l'extrait suivant:

Méthodes d'instance

... La capacité d'une sous-classe à remplacer une méthode permet à une classe d'hériter De D'une superclasse dont le comportement est "suffisamment proche", puis de modifier le comportement de Selon les besoins. ... Lors du remplacement d'une méthode, vous pouvez utiliser l'annotation @Override Pour indiquer au compilateur que vous souhaitez remplacer une méthode Dans la superclasse. Si, pour une raison quelconque, le compilateur détecte Que la méthode n'existe pas dans l'une des superclasses, il générera une erreur.

Passer outre peut être très déroutant car cela implique que la JVM prenne des mesures apparemment indépendantes. Rappelez-vous simplement que sans accès à la méthode [s] dans la superclasse (en d'autres termes, pour toutes les méthodes marquées private ), la sous-classe ne peut pas invoquer ni remplacer ladite méthode car t hériter pour le faire.

0
We_are_the_Borg_