Re: [問題] 多繼承super的問題

看板java作者 (十年一夢)時間9年前 (2015/01/03 00:06), 編輯推噓0(000)
留言0則, 0人參與, 最新討論串3/3 (看更多)
※ 引述《egheee (阿平)》之銘言: : class A { : void tell() { : Log.e("", "I am a"); : } : } : class B extends A{ : void tell() { : Log.e("", "I am b"); : } : } : class C extends B { : void tell() { : super.super.tell(); // 問題 : Log.e("", "I am c"); : } : } : 如上列所示,這樣的寫法是有問題的,super好像規定只能用一次 : 請問我要怎麼從C裡面call到A的tell()呢? 依所示的情境與需求,就我所知是無法單從 Java 語法層面去解決的。 有些版友提到在 C::tell method 裡另外建構一個 A instance,由此 instance 去調用 tell method。我想這應該只能針對上面範例之類的個案(tell method 對 調用的 object 本身沒有副作用),能適用的實例不多,不能算是一種解法。 如果不限制在語法層面,也暫時不考慮這樣的設計是否合理...等等,的確是有方式 可以轉寰的。我提供一個的可行做法是透過 bytecode 工程去加工。 先定義一下需求與條件,以上述的 sample 來說: 1. 你沒有 class A, class B 的 source code,只有 class file。 2. 有 class C 的 source 可供編輯。 3. 基於某種因素,你需要明確在 C::tell method 裡調用 A::tell implemention, 不論 C 的 base classes 有無 override tell method。 我做了一個 bytecode 加工的工具,可在此網址下載(jar file): http://ul.to/2k6o1dzz 將 C.java 修改為: import ptt.java.tool.RedirectSuperCall; class C extends B { @RedirectSuperCall(targetType=A.class) void tell() { super.tell(); Log.e("", "I am c"); } } 編譯 C.java 時將下載的 jar 加入 classpath。 編譯完後,執行 jar 內的 ptt.java.tool.Main class,執行時 classpath 除了 下載的 jar 外也需要包含你的 project binary(你的 project class file 不能 打包成 jar)。 java -cp <path_to_RedirectSuper.jar>;<project_output> ptt.java.tool.Main <path_to_C.class> 後面參數可以是(多個)檔案或檔案夾的路徑,如果檔案是 .class 檔案,tool 會去 看是否有使用 RedirectSuperCall annotation 來做加工,如果是檔案夾則會查看 檔案夾內的所有 .class 檔案(包括子檔案夾)。 為了比較快做出這個 tool 我有省略一些考量,算是比較簡化的版本。暫不考慮 generic method 以及 method exception clause,但 return type 與 parameter type/數量倒是沒有限制。 這個 tool 做的事情如下: 去修改以 RedirectSuperCall(targetType=XXX.class) 標注的 method bytecode (假設被標注的 method 是 someMethod),將 super.someMethod(...) expression 改成 invoke tool 注入在 XXX class 內的 static someMethod$hook method。 (被注入的 method 內容大致如下: class XXX { public static ... someMethod$hook(XXX obj, ...) { return obj.someMethod(...); // but use invokespecial instruction } } 雖然說 annotation 只標注在 C class,但是單只是去加工 C class bytecode 是 不足以做所需的效果,若是把 super.someMethod(...) expression 中 invokespecial instruction 的目標 class 從 B 改成 A,還是會執行到 B class 定義的 overriding 版本。(因為此時的 context 是 C class) 我的做法是把原來的 invokespecial call 改成一個 invokestatic call,而被 指定的 method 裡使用 invokespecial instruction 來調用真正想要調用的 non-static virtual method。 這裡 hook method 簡單地透過 invokespecial instruction 調用另一個 virtual method 的做法應該是比較不保險的做法,但我在幾種 JRE 上測試過都正確。 (如果有人測試時沒有成功 redirect,歡迎來信告知/分享你使用的 JRE) 比較保險的做法是把目標 method inline 在 hook method 裡,但若是 targetType 並沒有真的 declare/override 被標注的 method,實作上會很麻煩。所以是我偷懶 沒錯,但是額外好處是 targetType 可以指定任一個 base class,他的意思是 「我要 redirect 到 targetType 為止的最新版本」。 *tool bytecode version 是 50.0,故你需要 JRE 1.6+ 才能使用之。 **理論上,這個 tool 應該可以做成 annotation processor 成為編譯的一部份, 或是做成 instrumentation agent 在 runtime 時部署。前者我經驗還不夠,要搞 比較久;後者則是因為當 JVM 載入使用 annotation 標記的 class 時,他的 base class 已經載入,如果還要加工 base class 的話,必須要使用的 JVM 有支援 retransform。所以我做出了這樣的工具...呵呵,反正只是 demo 用途。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 1.172.233.127 ※ 文章網址: http://www.ptt.cc/bbs/java/M.1420214806.A.11E.html
文章代碼(AID): #1Kfi8M4U (java)
文章代碼(AID): #1Kfi8M4U (java)