[讨论] 方法重载的问题(方法一样,返回值不同)

richard_2010 2011-08-08
看到这样的两句话:
“同一类中不能存在两个名字及描述符完全相同的方法”,这个大家都好理解。
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”,这里意思是虽然我们.java文件中两个同名同参数类型,返回值不同的方法是通不过编译的,但是在class文件中却可以存在这样的两个方法。

Q:我怎么去验证后面这句话的正确性,即怎样让一个class文件中存在签名一样但返回值不同的两个方法?
chenjingbo 2011-08-08
    对于你的问题应该分成两个部分说,一个是编译器级别,还有一个是jvm级别
    “同一类中不能存在两个名字及描述符完全相同的方法” 这句话放在哪里都是对的.不管对于编译器还是对于jvm规范.因为描述符是包括方法参数和返回值的.
    “但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”这句话你注意一下,他说的是同样的特征签名,而不是之前说的名字和描述符.在官方自带的编译器里,它是拒绝给包含两个方法名和参数类型都一样的类编译的,但是在jvm规范中是允许某一个类包含"两个方法名和参数类型都一样,但是返回值不一样"的情况的.这一点,你可以通过字节码生成或者直接修改16进制文件的方式加以验证.将两个方法的方法名和参数类型设置成一样,但是返回值不一样.然后你会发现它是可以在jvm上跑起来的.
RednaxelaFX 2011-08-08
记得我以前一帖有讲这个 http://rednaxelafx.iteye.com/blog/479301
那帖用的BiteScript版本比较老,如果用新版本的BiteScript要稍微改改。

引用
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同

这个描述本来就是错的吧。楼上的分析没问题。
richard_2010 2011-08-08
chenjingbo 写道
    对于你的问题应该分成两个部分说,一个是编译器级别,还有一个是jvm级别
    “同一类中不能存在两个名字及描述符完全相同的方法” 这句话放在哪里都是对的.不管对于编译器还是对于jvm规范.因为描述符是包括方法参数和返回值的.
    “但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”这句话你注意一下,他说的是同样的特征签名,而不是之前说的名字和描述符.在官方自带的编译器里,它是拒绝给包含两个方法名和参数类型都一样的类编译的,但是在jvm规范中是允许某一个类包含"两个方法名和参数类型都一样,但是返回值不一样"的情况的.这一点,你可以通过字节码生成或者直接修改16进制文件的方式加以验证.将两个方法的方法名和参数类型设置成一样,但是返回值不一样.然后你会发现它是可以在jvm上跑起来的.


弱弱问一句,有什么工具可以修改字节码的生成或者改文件?
richard_2010 2011-08-08
RednaxelaFX 写道
记得我以前一帖有讲这个 http://rednaxelafx.iteye.com/blog/479301
那帖用的BiteScript版本比较老,如果用新版本的BiteScript要稍微改改。

引用
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”

这个描述本来就是错的吧。楼上的分析没问题。


thanks,看贴去。
RednaxelaFX 2011-08-08
richard_2010 写道
弱弱问一句,有什么工具可以修改字节码的生成或者改文件?

有一种弱弱的办法是把Class文件反编译成Jasmin格式的文本,修改它,然后用Jasmin重新生成Class文件。可用的反编译器用"jasmin java disassembler"这些关键字就能搜出一些。
问题是Jasmin很老,我印象是Java 5之后的Class文件它处理不了。

另外最常见的当然还是写代码用ASM来直接操纵字节码。
richard_2010 2011-08-08
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”
--这句话哪里错了?

我想到了一个很简单的方法来验证这点了:
public static void main(String[] args) {
		System.out.println(test(new ArrayList<String>()));
		System.out.println(test(new ArrayList<Integer>()));

	}
	
	public static int test(List<Integer> arg) {
		return 0;
	}
	
	public static String test(List<String> arg) {
		return "";
	}


编译的时候会将泛型擦除,这样两个test方法就变成了

       public static int test(List arg) {
		return 0;
	}
	
	public static String test(List arg) {
		return "";
	}


这样除了返回值外,别的都一样,事实证明这段代码是能跑成功的。
RednaxelaFX 2011-08-08
richard_2010 写道
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”
--这句话哪里错了?

在“Class文件”这个上下文里,signature本来就包含返回值类型…所以这句话是前后矛盾的。用来表现signature的就是NameAndType,方法的话,Type里就有返回值类型。Class文件里不存在只有参数列表类型而没有返回值类型的方法类型描述符。

Java语言JVM对signature的认识有差异。这个区分清楚就好了。
qianhd 2011-08-08
richard_2010 写道
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”
--这句话哪里错了?

我想到了一个很简单的方法来验证这点了:
public static void main(String[] args) {
		System.out.println(test(new ArrayList<String>()));
		System.out.println(test(new ArrayList<Integer>()));

	}
	
	public static int test(List<Integer> arg) {
		return 0;
	}
	
	public static String test(List<String> arg) {
		return "";
	}


编译的时候会将泛型擦除,这样两个test方法就变成了

       public static int test(List arg) {
		return 0;
	}
	
	public static String test(List arg) {
		return "";
	}


这样除了返回值外,别的都一样,事实证明这段代码是能跑成功的。



List<String> 和 List<Integer> 在编译时期 是2个不同类型
事实上 这个重载的限制是编译器的 不是虚拟机的
IcyFenix 2011-08-08
richard_2010 写道
看到这样的两句话:
“同一类中不能存在两个名字及描述符完全相同的方法”,这个大家都好理解。
“但在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同”,这里意思是虽然我们.java文件中两个同名同参数类型,返回值不同的方法是通不过编译的,但是在class文件中却可以存在这样的两个方法。

Q:我怎么去验证后面这句话的正确性,即怎样让一个class文件中存在签名一样但返回值不同的两个方法?


LZ这个拿的是我书上的例子吧。

书上提到“同一个Class文件”的原文是“在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存”。后面还有跟着一句提到了相同的特征签名,原文为“两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是可以合法地共存于一个Class文件中”,但这句话加了下面这个脚注:

注3:在《Java虚拟机规范第二版》(JDK 1.5修改后的版本)的“§4.4.4 Signatures”章节及《Java语言规范第三版》的“§8.4.2 Method Signature”章节中分别都定义了字节码层面的方法特征签名,以及Java代码层面的方法特征签名,特征签名最重要的任务就是作为方法独一无二不可重复的ID,在Java代码中的方法特征签名只包括了方法名称、参数顺序及参数类型,而在字节码中的特征签名还包括方法返回值及受查异常表,本书中如果指的是字节码层面的方法签名,笔者会加入限定语进行说明,也请读者根据上下文语境注意区分。

另外,这个story还有后续的进展:在JDK7中,javac的行为和ejc的一致了,这段代码都不能编译通过。
Global site tag (gtag.js) - Google Analytics