日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

vector父类类型可以存放子类吗_拼夕夕三轮面经:被问到反射和泛型的bug,你踏空了吗?...

發布時間:2024/7/23 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vector父类类型可以存放子类吗_拼夕夕三轮面经:被问到反射和泛型的bug,你踏空了吗?... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? 點擊上方“JavaEdge”,關注公眾號

設為“星標”,好文章不錯過!

1 當反射遇見方法重載

重載grade方法,入參分別為int、Integer。若不通過反射這種高級編程方式,選用哪個重載方法自然很清晰,比如傳666走int參數重載方法,傳入Integer.valueOf(“666”)走Integer重載。

但你若墨守成規認為反射調用方法也是根據入參類型確定方法重載,那就掉坑了。
使用getDeclaredMethod獲取?grade方法,然后傳入Integer.valueOf(“36”)

因為通過反射進行方法調用首先是

通過方法簽名來確定方法

本例的getDeclaredMethod傳入的參數類型Integer.TYPE其實一直代表int。

所以實際執行方法時傳包裝類型、基本類型,最終都是調用int入參的grade方法。

修正方案

將Integer.TYPE改為Integer.class,實際執行的參數類型就是Integer了。且無論傳包裝類型/基本類型,最終都會調用Integer為入參的grade方法。

所以反射調用方法,是以反射獲取方法時傳入的方法名和參數類型來確定調用的方法。

2 當泛型因類型擦除遇見橋接方法

泛型作為一種編程范式,使得開發者可以使用類型參數替代精確類型,實例化時再指明具體類型。也利于代碼重用,將一套代碼應用到多種數據類型

泛型的類型檢測,可以在編譯時暴露大多數泛型編碼錯誤。但由于歷史兼容性而妥協的泛型類型擦除,在運行時才會暴露很多坑。

案例

期望在類字段內容變動時記錄日志,于是開發同學就想到定義一個泛型父類,并在父類中定義一個統一的日志記錄方法,子類可繼承該方法。上線后總出現日志重復記錄問題。

父類

子類Child1 未提供父類泛型參數且定義了一個參數為String而非T的setValue。期望覆蓋父類的setValue實現。

子類方法的調用是通過反射。

雖Parent的value字段正確設置JavaEdge,但父類setValue調用了兩次,計數器而顯示2兩次Parent的setValue方法調用,是因為getMethods找到了兩個setValue的,分屬于父類/子類。

子類重寫父類方法失敗原因

  • 子類未指定String泛型參數,父類的泛型方法setValue(T value)泛型擦除后是setValue(Object value),于是子類入參String的setValue被當作了新方法

  • 子類的setValue方法未加@Override注解,編譯器未能檢測到重寫失敗。

重寫子類方法時,務必使用@Override注解。

但有人認為問題是反射API使用不當而未意識到重寫失敗。查文檔后才發現

  • getMethods能獲得當前類和父類的所有public方法

  • getDeclaredMethods僅獲得當前類所有的public、protected、package和private方法

于是用getDeclaredMethods替換getMethods:

這雖能解決重復記錄日志,但未解決子類重寫父類方法失敗,日志:

當其他人使用Child1時還是會發現有倆setValue,讓人困惑。

重新實現Child2,繼承Parent時String作為泛型T類型,并使用@Override注解setValue,實現有效的方法重寫

還是出現重復日志

Child2的setValue調了兩次。難道是JDK的反射出Bug了!
通過getDeclaredMethods查找到的方法肯定來自Child2本身;而且Child2類中看起來也只有一個setValue,怎么可能還重復?

調試發現,Child2類其實有倆setValue:入參分別是String/Object。

這就是泛型類型擦除導致。

解密反射下的泛型擦除天坑

Java泛型類型在編譯后被擦除為Object。子類雖指定父類泛型T類型是String,但編譯后T會被擦除成為Object,所以父類setValue入參是Object,value也是Object。
若Child2?setValue想覆蓋父類,那入參也須為Object。所以,編譯器會為我們生成一個橋接方法

Child2類的class字節碼:

若編譯器未幫我們實現該橋接方法,那Child2重寫的是父類泛型類型擦除后、入參是Object的setValue。這倆方法參數,一個String一個Object,明顯不符合Java語義:

class Parent {
AtomicInteger updateCount = new AtomicInteger();private Object value;public void setValue(Object value) {
System.out.println("調用 Parent 的 setValue");this.value = value;
updateCount.incrementAndGet();}}class Child2 extends Parent {@Overridepublic void setValue(String value) {
System.out.println("調用 Child2 的 setValue");super.setValue(value);}}

驗證:使用jclasslib打開Child2,可看到入參為Object的橋接方法上標記public synthetic bridge。synthetic代表由編譯器生成的不可見代碼,bridge代表這是泛型類型擦除后生成的橋接代碼

修正方案

使用method的isBridge方法,來判斷方法是不是橋接方法:

  • 通過getDeclaredMethods方法獲取到所有方法后,必須同時根據方法名setValue和非isBridge兩個條件過濾,才能實現唯一過濾

  • 使用Stream時,如果希望只匹配0或1項的話,可以考慮配合ifPresent來使用findFirst方法。

使用反射查詢類方法清單時:

  • getMethods和getDeclaredMethods是有區別的,前者可以查詢到父類方法,后者只能查詢到當前類

  • 反射進行方法調用要注意過濾橋接方法。

往期推薦

大廠如何解決數值精度/舍入/溢出問題

大廠數據庫事務實踐-事務生效就能保證正確回滾?

線上問題事跡(一)數據庫事務居然都沒生效?

硬核干貨:HTTP超時、重復請求必見坑點及解決方案

給大忙人們看的Java NIO教程之Channel

目前交流群已有?800+人,旨在促進技術交流,可關注公眾號添加筆者微信邀請進群

喜歡文章,點個“在看、點贊、分享”素質三連支持一下~

總結

以上是生活随笔為你收集整理的vector父类类型可以存放子类吗_拼夕夕三轮面经:被问到反射和泛型的bug,你踏空了吗?...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。