Can I override a private method in Java?

2019-01-11 11:22发布

问题:

I know I can use reflection to invoke a private method, and to get or set the value of a private variable, but I want to override a method.

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;
    }
}

I want the main method in SubClass to print out 2, but it prints out 1. I've heard this can be done through reflection, but I can't figure out how. If not reflection, does anyone know of another way of doing it? (Other than making SuperClass.getInt() protected, or copying and pasting the printInt() method into SubClass.) If actually overriding the private method is not possible, is there a way of placing some sort of trigger on it that will invoke a method in my sub-class either before or after the private method executes?

回答1:

Private methods are not inherited and cannot be overridden in any way. Whoever told you you can do it with reflection was either lying or talking about something else.

However, you can access the private method getInt of whatever subclass is invoking printInt like so:

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

This will have the effect of the subclass' getInt method being called from the superclass' printInt. Of course, now this will fail if the subclass doesn't declare a getInt, so you have to add a check to be able to handle "normal" subclasses that don't try to "override" a private method:

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) );
}

You still have to change superclass code to make this work, and since you have to change superclass code, you should just change the access modifier on getInt to protected instead of doing reflection hack-arounds.



回答2:

You can't override a private method because no other class, including a derived class, can tell that it exists. It's private.

Private methods are implicitly final.

On a related note, a subclass can declare a field or method with the same name as a private field or method in a super class, because from the subclass's point of view, these members do not exist. There's no special relationship between these members.



回答3:

No, you can't. You can construct a program which look like it should be able to do this, using the fact that code within an outer class can access nested class's private members. However, private methods still can't actually be overridden. Example:

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();
    }
}

Given that this would be the only situation in which it was feasible to override a private method, it's no great loss that it's not supported.

If you want to change the behaviour of a private member of your superclass, your design is broken, basically.



回答4:

I couldn't do this with reflection, but I have figured out a way to do it via instrumentation. I've used ASM, as suggested on Dhruba Bandopadhyay's blog. If you're interested, you can look at my code (there's too much to post it here).



回答5:

If you really wanted to do this using reflection, you could:

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();
    }
}

I suspect you want to design your program differently, rather than take this approach. It's not performant, and it will definitely catch anybody who extends SuperClass by surprise.

Note that the subclass's getInt() method can be private if you use reflection this way. Again, taking this approach is almost definitely a Bad Idea, for at least the two reasons mentioned. Chances are you can accomplish your goals another way. Package protection is really not an option?



回答6:

can you pass var by reference -> classes using java ?

because if it's ever possible: above problem could be rewritten as:

(Note this is for specification purpose only)

VB:

function(ByRef intX as Integer) //value can be explicitly changed but not the method itself :P end function

so much more like that i guess...



回答7:

No we cannot override Private methods, but there is a case where we can override private methods too i.e. 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();
}

}



回答8:

You can't override a private method, but you can introduce/redefine one in a derived class.Like:

class Super
{
   private void fun()
   {
   }
}

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

In this you are not overriding this fun() in the Child class you are defining a new method with the same name but if you try to use @override on the top of fun() of Child class than you will get compile time error because you are forcing to override this fun() else you are just redefining the new fun() in Child class



回答9:

You can't override a private method.

An overriding method can only exist in a subclass of the overridden method. If the method that you want to override is marked private, overriding it is not possible because the subclass doesn't inherit anything marked private, thus severing ties between that private method and anything in the subclass, including any of the subclass's methods.

To "override" means allowing the JVM to determine which method to use based on the instance type that you are invoking it with. The theory of polymorphism explains what makes this possible. In order for the JVM to be able to say "these methods are connected, one overrides the other", the JVM must be able to view the overridden method from within the subclass that contains the overriding method.

That being said, the classes in the case you provided will still compile, but neither of the methods will be invoked based on the JVMs independently run instance-based invocation. They will merely be different methods altogether that fail to be recognized by the JVM as overridden or overriding.

Furthermore, if you are using an IDE such as NetBeans, using the @Override annotation will instruct the compiler that you are overriding the method with the same signature in the superclass. If you don't do this, you will get a warning suggesting that you make the annotation. You will get a compilation error, however, if you apply this annotation when:

  • there is no method with a matching signature in the superclass

OR

  • the method in the superclass that you are trying to override is marked private or final

Refer to the official Oracle documentation https://docs.oracle.com/javase/tutorial/java/IandI/override.html for the following excerpt:

Instance Methods

...The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed.... When overriding a method, you might want to use the @Override annotation that instructs the compiler that you intend to override a method in the superclass. If, for some reason, the compiler detects that the method does not exist in one of the superclasses, then it will generate an error.

Overriding can be very confusing because it involves the JVM taking action seemingly independently. Just remember that without access to the method[s] in the superclass (in other words, for all methods marked private), the subclass can't invoke or override the said method[s] because it can't inherit them to do so.



回答10:

Read this carefully,

PRIVATE methods are NOT INHERITED... As they are not inherited, they CANNOT BE OVERRIDDEN