我可以覆盖Java中的私有方法吗?

时间:2012-05-16 17:49:37

标签: java reflection polymorphism access-modifiers

我知道我可以使用反射来调用私有方法,并获取或设置私有变量的值,但我想覆盖一个方法。

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

我希望main中的SubClass方法打印出2,但会打印出1。 我听说这可以通过反思完成,但我无法弄清楚如何。 如果没有反思,有没有人知道另一种方式呢? (除了使SuperClass.getInt()受保护,或将printInt()方法复制并粘贴到SubClass之外。) 如果实际上无法覆盖私有方法,是否有办法在其上放置某种触发器,它将在私有方法执行之前或之后调用子类中的方法?

10 个答案:

答案 0 :(得分:17)

您不能覆盖私有方法,因为没有其他类(包括派生类)可以判断它是否存在。这是私人。

私有方法隐式final

在相关的说明中,子类可以声明一个与超类中的private字段或方法同名的字段或方法,因为从子类的角度来看,这些成员不存在。这些成员之间没有特殊的关系。

答案 1 :(得分:14)

私有方法不会被继承,也不能以任何方式覆盖。无论谁告诉你,你都可以用反思来做这件事,或者说谎或谈论其他事情。

但是,您可以访问调用getInt的子类的私有方法printInt,如下所示:

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

这将具有从超类“getInt调用子类”printInt方法的效果。 当然,如果子类没有声明getInt,现在这将失败,所以你必须添加一个检查,以便能够处理不尝试的“普通”子类“覆盖”私有方法:

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

您仍然需要更改超类代码才能使其工作,并且由于您必须更改超类代码,因此您只需将getInt上的访问修饰符更改为protected,而不是执行反射黑客攻击

答案 2 :(得分:13)

不,你不能。您可以使用外部类中的代码可以访问嵌套类的私有成员这一事实来构建一个看起来的程序,就像它应该能够这样做一样。但是,私有方法实际上仍然无法被覆盖。例如:

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

鉴于这将是唯一的情况,在这种情况下可以覆盖私有方法,所以不支持它不会有很大的损失。

如果你想改变你的超类私人成员的行为,你的设计基本上就会被打破。

答案 3 :(得分:5)

我不能用反射做到这一点,但我已经找到了通过仪器来做到这一点的方法。我按照Dhruba Bandopadhyay's blog的建议使用了ASM。如果您有兴趣,可以查看my code(这里发布的内容太多了。)

答案 4 :(得分:2)

如果你真的想用反射来做这件事,你可以:

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

我怀疑你想以不同的方式设计你的程序,而不是采用这种方法。它不具备高性能,它肯定会让任何超越SuperClass的人感到惊讶。

请注意,如果以这种方式使用反射,则子类的getInt()方法可以是私有的。再次,至少提到的两个原因,采取这种方法几乎肯定是一个坏主意。您可以通过另一种方式实现目标。包保护真的不是一种选择吗?

答案 5 :(得分:0)

你可以通过引用传递var - &gt;使用java的类?

因为如果有可能的话: 上述问题可以改写为:

(注意这仅用于说明目的)

VB:

函数(ByRef intX as Integer)    //值可以显式更改,但不能显示方法本身:P 结束功能

更像我猜...

答案 6 :(得分:0)

您无法覆盖私有方法,但您可以在派生类中引入/重新定义。方法:

class Super
{
   private void fun()
   {
   }
}

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

在这里,你没有覆盖Child类中的这个fun(),你正在定义一个具有相同名称的新方法但是如果你尝试在child类的fun()之上使用@override而不是你将得到编译时间错误,因为你强迫覆盖这个fun()否则你只是在Child类中重新定义新的fun()

答案 7 :(得分:0)

您无法覆盖私有方法。

重写方法只能存在于重写方法的子类中。如果您要覆盖的方法标记为私有,则无法覆盖它,因为子类不会继承任何标记为私有的内容,从而切断该私有关系方法和子类中的任何东西,包括任何子类的方法。

To&#34;覆盖&#34;表示允许JVM根据您调用它的实例类型确定使用哪种方法。多态性理论解释了什么使这成为可能。为了让JVM能够说'#34;这些方法是连接的,一个覆盖另一个&#34;,JVM必须能够从内部查看重写的方法包含重写方法的子类

话虽如此,您提供的案例中的类仍将进行编译,但基于JVM独立运行基于实例的调用将不会调用这两种方法。它们只是完全不同的方法,无法被JVM识别为被覆盖或覆盖。

此外,如果您使用的是NetBeans等IDE,则使用@Override注释将指示编译器您使用超类中的相同签名覆盖该方法。如果您不这样做,您将收到警告,建议您进行注释。但是,如果在以下情况下应用此批注,则会出现编译错误:

  • 超类中没有匹配签名的方法

OR

  • 您尝试覆盖的超类中的方法标记为私有最终

请参阅官方Oracle文档 https://docs.oracle.com/javase/tutorial/java/IandI/override.html 以下摘录:

  

实例方法

     

...子类覆盖方法的能力允许类继承   从   一个超类,其行为足够接近&#34;然后修改   根据需要行为....   覆盖方法时,您可能希望使用@Override   指示编译器您要覆盖的注释   超类中的方法。如果由于某种原因,编译器检测到   该方法在其中一个超类中不存在,那么它   会产生错误。

覆盖可能非常混乱,因为它涉及JVM似乎独立地采取行动。请记住,如果不访问超类中的方法[s](换句话说,对于标记为 private 的所有方法),子类不能调用或覆盖所述方法[s],因为它不能继承它们。

答案 8 :(得分:-1)

不,我们不能覆盖私有方法,但有一种情况我们也可以覆盖私有方法,即内部类

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

}

答案 9 :(得分:-4)

仔细阅读,

私有方法不是固有的...因为它们不是继承的,所以它们不能超载