我知道我可以使用反射来调用私有方法,并获取或设置私有变量的值,但我想覆盖一个方法。
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
之外。)
如果实际上无法覆盖私有方法,是否有办法在其上放置某种触发器,它将在私有方法执行之前或之后调用子类中的方法?
答案 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)
仔细阅读,
私有方法不是固有的...因为它们不是继承的,所以它们不能超载