我有兩節課
// BaseClass.java
class BaseClass<T> {
public String getTest(){
return "one";
}
public String getTest2(T t){
return "two";
}
public String getTest3(T t){
return "three";
}
}
// OverrideClass.java
public class OverrideClass extends BaseClass<Test>{
}
我嘗試運行以下代碼
// Test.java
public class Test {
public static void main(String[] args) {
Class<OverrideClass> overrideClass = OverrideClass.class;
Method[] declaredMethods = overrideClass.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethods));
}
}
我認為它應該輸出
[]
但實際上輸出是
[public java.lang.String OverrideClass.getTest()]
通過位元組碼,我認為這是一個橋接方法,但我不知道它為什么會生成,如果我將 BaseClass 公開它會消失。
// access flags 0x1041
public synthetic bridge getTest()Ljava/lang/String;
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL BaseClass.getTest ()Ljava/lang/String;
ARETURN
L1
LOCALVARIABLE this LOverrideClass; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
}
我的問題是:
- 如果 BaseClass 是默認值,為什么要
getTest()生成橋接方法? - 為什么
getTest2()并getTest3()沒有生成他們的橋接方法?這似乎與泛型有關。
謝謝你的幫助!
uj5u.com熱心網友回復:
我已經分析了這個問題,這是結果。我已經簡化了問題中的示例。
這個答案處理OP的第一個問題
如果 BaseClass 是默認值,為什么 getTest() 會生成橋接方法?
關于與泛型出現不一致的第二個問題,您可以閱讀丹尼斯的回答
示例 1
class BaseClass {
public String getTest(){
return "one";
}
public String getTest2(){
return "two";
}
public String getTest3(){
return "three";
}
}
public class OverrideClass extends BaseClass{}
public class Application {
public static void main(String[] args) throws Exception {
Class<OverrideClass> overrideClass1 = OverrideClass.class;
Method[] declaredMethods1 = overrideClass1.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethods1));
}
}
使用 JDK 8 或 JDK 17 執行此操作始終具有相同的結果
[public java.lang.String OverrideClass.getTest(), public java.lang.String OverrideClass.getTest2(), public java.lang.String OverrideClass.getTest3()]
示例 2
只需將上面的例子修改成
public class BaseClass {
public String getTest(){
return "one";
}
public String getTest2(){
return "two";
}
public String getTest3(){
return "three";
}
}
請注意,更改是在 Base 類的訪問修飾符中,現在是public!
執行此操作會產生預期的行為 []
然而,這不是 JDK 的錯誤。它是這樣設計的。
解釋
在 example1getDeclaredMethods()中回傳父類的相同方法的原因不是因為這些方法被列印為繼承。這是因為這些是實際上屬于該子類(OverrideClass)的橋接方法。
此功能已在很久以前添加,您可以從 oracle 的開發人員那里看到的解釋是
建議是在這些非常罕見的情況下添加橋接方法來解決反射中的問題,而沒有其他可預見的修復或解決方法。具體來說,當公共方法從非公共類繼承到公共類時,我們將生成一個橋接方法。
正如您在這里所看到的,oracle 開發人員的最新評論是
在這種情況下添加橋方法,其中來自非公共超類的公共類公共方法允許對子類方法進行反射訪問的可能性 JDK-6342411)。
關閉這個問題不是一個錯誤。
所以這只發生在非公共父類中,因為在這種情況下,需要將繼承的公共方法添加為該子類中的橋接方法。
在不存在橋接方法的示例 2javap -c OverrideClass中,如果您嘗試使用列印出反匯編代碼,您將看到以下內容
public class OverrideClass extends BaseClass {
public OverrideClass();
Code:
0: aload_0
1: invokespecial #1 // Method BaseClass."<init>":()V
4: return
}
在存在橋方法的示例 1中,如果您嘗試使用列印出反匯編代碼,javap -c OverrideClass您將看到以下內容
public class OverrideClass extends BaseClass {
public OverrideClass();
Code:
0: aload_0
1: invokespecial #1 // Method BaseClass."<init>":()V
4: return
public java.lang.String getTest3();
Code:
0: aload_0
1: invokespecial #7 // Method BaseClass.getTest3:()Ljava/lang/String;
4: areturn
public java.lang.String getTest2();
Code:
0: aload_0
1: invokespecial #11 // Method BaseClass.getTest2:()Ljava/lang/String;
4: areturn
public java.lang.String getTest();
Code:
0: aload_0
1: invokespecial #14 // Method BaseClass.getTest:()Ljava/lang/String;
4: areturn
}
uj5u.com熱心網友回復:
-
如果 BaseClass 是默認值,為什么 getTest() 會生成橋接方法?
請參閱Panagiotis Bougioukos 的回答。
-
為什么 getTest2() 和 getTest3() 沒有生成它們的橋接方法?這似乎與泛型有關。
這是一個錯誤,來自JDK-8216196 的描述。沒有為通用方法創建可見性橋梁:
如果它們是公共的但由包私有類宣告,javac 通常會為方法添加可見性橋:
如果方法具有泛型型別,則不是這種情況:
該錯誤已由JDK-8203488 修復。從 TransTypes 中洗掉錯誤生成,向后移植到 11.0.1
示例,運行結果
package org.example;
class BaseClass<T> {
public String getTest() {
return "one";
}
public String getTest2(T t) {
return "two";
}
public String getTest3(T t) {
return "three";
}
}
package org.example;
public class OverrideClass extends BaseClass<Void> {
}
package org.example.other; // Note other package
import org.example.OverrideClass;
import java.lang.reflect.Method;
import java.util.Arrays;
public class OverrideClass2 extends OverrideClass {
public static void main(String[] args) throws Exception {
Arrays.stream(OverrideClass.class.getDeclaredMethods())
.forEach(System.out::println);
invokeGetTestReflectively();
System.out.println("invoked 'getTest' reflectively");
invokeGetTest2Reflectively();
System.out.println("invoked 'getTest2' reflectively");
}
private static void invokeGetTestReflectively() throws Exception {
Method method = OverrideClass2.class.getMethod("getTest");
method.invoke(new OverrideClass2());
}
private static void invokeGetTest2Reflectively() throws Exception {
Method method = OverrideClass2.class.getMethod("getTest2", Object.class);
method.invoke(new OverrideClass2(), new Object[]{null});
}
}
運行結果
Java 8,openjdk 版本“1.8.0_332”
public java.lang.String org.example.OverrideClass.getTest()
invoked 'getTest' reflectively
Exception in thread "main" java.lang.IllegalAccessException: Class org.example.other.OverrideClass2 can not access a member of class org.example.BaseClass with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at org.example.other.OverrideClass2.invokeGetTest2Reflectively(OverrideClass2.java:26)
at org.example.other.OverrideClass2.main(OverrideClass2.java:15)
- 允許反射訪問,但 javac 中存在錯誤
Java 17,openjdk 版本“17.0.3” 2022-04-19 LTS
public java.lang.String org.example.OverrideClass.getTest()
public java.lang.String org.example.OverrideClass.getTest2(java.lang.Object)
public java.lang.String org.example.OverrideClass.getTest3(java.lang.Object)
invoked 'getTest' reflectively
invoked 'getTest2' reflectively
- 允許反射訪問,javac 中沒有錯誤
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/481022.html
