为何javac会生成clone方法?Java如何链接同签名方法?
Java中Cloneable接口与clone方法的编译与链接疑问
问题重现
首先定义一个实现Cloneable接口的类:
public class T1 implements Cloneable { private T1(T1 g) {} public T1 clone() { return new T1(this); } }
编译后通过javap -s T1查看字节码,会发现类中存在两个clone方法:
$ javac T1.java $ javap -s T1 Compiled from "T1.java" public class T1 implements java.lang.Cloneable { public T1 clone(); descriptor: ()LT1; public java.lang.Object clone() throws java.lang.CloneNotSupportedException; descriptor: ()Ljava/lang/Object; }
但如果手动在代码中同时定义这两个方法,会触发编译错误:
public class T1 implements Cloneable { private T1(T1 g) {} public T1 clone() { return new T1(this); } public Object clone() { return null; } }
编译输出如下:
$ java T1.java T1.java:7: error: method clone() is already defined in class T1 public Object clone() { return (null); }
核心疑问
- 为什么javac会自动生成第二个返回类型为
Object的clone方法? - Java是如何处理输入签名相同但返回类型不同的方法的链接逻辑?
解答
1. 自动生成Object类型clone方法的原因
这是Java协变返回类型特性结合Object.clone()特殊规则的结果。
当你在实现Cloneable的类中定义了返回子类类型的clone()方法时,javac会自动生成一个桥接方法。这个桥接方法的作用是兼容父类Object中定义的public Object clone() throws CloneNotSupportedException方法签名:
- Java语言层面不允许直接定义两个仅返回类型不同的方法,否则会触发编译错误;
- 但JVM的方法描述符(完整签名)包含返回类型,因此需要这个桥接方法来满足JVM的方法查找规则,同时保证Java代码的语法合法性。
桥接方法内部会调用你自定义的T1 clone()方法,再将结果向上转型为Object返回,以此实现对父类方法签名的兼容。
2. 输入签名相同方法的链接逻辑
Java语言层面不允许两个方法仅返回类型不同,但JVM的方法描述符(完整签名)是包含返回类型的——也就是说,JVM会把()LT1;和()Ljava/lang/Object;视为两个完全不同的方法。
在链接阶段,JVM会根据完整的方法描述符查找对应方法:
- 当调用者的静态类型是
T1时,编译器生成调用()LT1;方法的字节码; - 当调用者的静态类型是
Object时,编译器生成调用()Ljava/lang/Object;方法的字节码。
编译器生成的桥接方法,就是为了保证当代码通过Object类型调用clone()时能正确找到实现,同时让子类自己的强类型clone()方法可以被直接调用。
内容的提问来源于stack exchange,提问作者Павел
相关产品推荐
相关产品推荐

