本文分析Java泛型中使用方法引用時,特別是涉及繼承關(guān)系時可能遇到的一個常見問題:父類泛型方法引用子類方法,編譯后卻調(diào)用了父類方法。 我們將探討這個問題的根源以及有效的解決方法。
假設(shè)我們有三個類:Car、redCar和YellowCar,其中RedCar和YellowCar繼承自Car:
@Data public class Car { private String id; private int status; } @Data public class RedCar extends Car { } @Data public class YellowCar extends Car { }
在BaseCarController中,我們希望通過泛型T調(diào)用不同子類的getStatus方法:
立即學(xué)習(xí)“Java免費(fèi)學(xué)習(xí)筆記(深入)”;
public class BaseCarController<T extends Car> { @Autowired CommonService cs; public void test(int id) { // 這里的問題:編譯器會選擇Car的getStatus方法 cs.toggle(id, T::getStatus); } } public class RedCarController extends BaseCarController<RedCar> {} public class YellowCarController extends BaseCarController<YellowCar> {}
預(yù)期中,RedCarController調(diào)用test方法時,應(yīng)該執(zhí)行cs.toggle(id, RedCar::getStatus);YellowCarController調(diào)用test方法時,應(yīng)該執(zhí)行cs.toggle(id, YellowCar::getStatus)。然而,由于Java泛型的類型擦除機(jī)制,編譯器在編譯BaseCarController時,T::getStatus會被替換成Car::getStatus,導(dǎo)致實(shí)際執(zhí)行結(jié)果與預(yù)期不符,進(jìn)而影響數(shù)據(jù)庫更新(例如使用mybatis-Plus)。
解決方法:使用實(shí)例方法引用
為了避免類型擦除帶來的問題,我們可以使用實(shí)例方法引用代替靜態(tài)方法引用。 需要將方法的參數(shù)修改為具體的Car對象實(shí)例,然后使用實(shí)例方法引用。
public class Car { public String getStatus() { return "Car::status"; } } class RedCar extends Car { @Override public String getStatus() { return "RedCar::status"; } } public class Controller<T extends Car> { public void test(T car) { invoke(car::getStatus); // 實(shí)例方法引用 } private void invoke(Supplier<String> supplier) { System.out.println(supplier.get()); } }
現(xiàn)在,通過傳入具體的Car對象實(shí)例,例如RedCar redCar = new RedCar(); new Controller().test(redCar);,就能正確調(diào)用RedCar的getStatus方法。 CommonService.toggle方法也需要相應(yīng)修改,接受一個Supplier