好的,我會盡量清楚地解釋這一點。
我創建了一個通用抽象控制器類,它的方法hasCreatePermissions如下所示:
public abstract class ApplicationController<
AppEntity extends ApplicationEntity,
AppService extends ApplicationService<AppEntity>,
DTOManager extends ApplicationDTOManager
> {
// Other methods, properties, etc...
public boolean hasCreatePermissions(DTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
return false;
}
}
本質上,我希望任何重寫此方法的類在重寫此方法時都能夠使用自己的 DTOManager 類作為引數。
通用 ApplicationDTOManager 類看起來像
public abstract class ApplicationDTOManager {
public abstract class CreationRequest {}
public abstract class CreationResponse {}
}
任何繼承 ApplicationDTOManager 的類都可以添加擴展 CreationRequest 和 CreationResponse 的類,以實作各自的 DTO。
但是,假設我嘗試使用 UserResource 類對其進行擴展(假設 UserDTOManager 存在并具有 CreationRequest 的實作):
@RestController
public class UserResource extends ApplicationController<
User,
UserService<User>,
UserDTOManager
> {
@Override
public boolean hasCreatePermissions(UserDTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
// Stuff
}
}
我被告知這不會覆寫任何超類方法。為什么?有什么方法可以實作這一點,因為我不想將太多泛型傳遞給我的 ApplicationController 類,但也不能有建構式。
uj5u.com熱心網友回復:
class ApplicationController< AppEntity extends ApplicationEntity,
不,停在那里。這是使用系結規則宣告一個型別變數:“必須是其中一個ApplicationEntity或任何子型別”并且您將其命名為AppEntity。當您稍后閱讀代碼時,這會讓您頭暈目眩,“我一直將具體型別與型別變數混淆”占所有泛型混淆的 95%。我知道這似乎不可讀,但實際上只有一種方法可以解決,那就是對所有型別變數使用單個大寫字母。所以讓我們現在解決這個問題:
public abstract class ApplicationController<
E extends ApplicationEntity,
S extends ApplicationService<E>,
M extends ApplicationDTOManager> {
然后立即讓我們在下一行看到一個大問題:
public boolean hasCreatePermissions(M.CreationRequest requestBody) {}
這當然行不通。M是一個型別變數,在編譯時編譯器不知道它是什么型別。它可能會系結到一些現在還不存在的型別。您不能在型別變數上要求具體的內部型別。您當然可以簡單地談論ApplicationDTOManager.CreationRequest,這大概就是您想要的。
或者,您認為 ApplicationDTOManager 的子型別也可能想要子類化ApplicationDTOManager.CreationRequest. 這是可能的,但是所有 ApplicationDTOManager 型別都需要攜帶它們關??聯的 CreationRequest 型別作為型別變數。因此,我們修復了一些問題:
public class ApplicationDTOManager<R extends ApplicationDTOManager.CreationRequest> {
public static class CreationRequest {}
}
您可能有一個名為 CreationRequest 的非靜態內部類。我要再次阻止你 - 非靜態內部有一個不可見的外部型別的內部欄位,并且將這種不可見的巫毒魔法與泛型結合起來是行不通的。不要這樣做。如果必須擁有該欄位,則可以顯式創建該欄位,并創建一個接受它的建構式。如果您不添加static到您的內部類,這就是 javac 為您生成的內容。但是通過明確說明,你可以控制泛型,你必須在這里,并避免混淆,因為問題的性質似乎是相關的。
我被告知這不會覆寫任何超類方法。為什么?
Java 的方法名稱包括所有被擦除的型別。此方法的名稱:
class List<T extends Number> {
int hello(String name, boolean[] hi, T arg) throws SQLException {}
}
是,就 JVM 而言,hello(Ljava/lang/String;[ZLjava/lang/Number;)I.
是的,不,真的。javap -c -v一個類檔案,你會看到它。(I是整數,[是陣列,Z是布林值,并Ltxt;以 JVM 樣式編碼 ref 型別名稱,例如用斜杠和美元而不是點)。是寫的name(params)ret。
如果您隨后對某些內容進行子型別化并引入洗掉的 JVM 名稱相同的方法,那么您就是在覆寫。如果你不這樣做,它不是一個覆寫。簡直是超載。覆寫是動態調度的。但多載不是- 名稱都在編譯時鏈接起來。但是,對于任何給定的“JVM 方法名稱”,查找是根據接收器型別動態完成的。換句話說:
class Fruit {
void hi(Fruit f) { System.out.println("Fruit Fruit"); }
void hi(Apple a) { System.out.println("Fruit Apple"); }
}
class Apple extends Fruit {
void hi(Fruit f) { System.out.println("Apple Fruit"); }
void hi(Apple a) { System.out.println("Apple Apple"); }
}
Fruit f = new Fruit();
Fruit a = new Apple();
a.hi(a);
將列印Apple Fruit。你Apple Apple可能認為它應該列印出來——我們稱蘋果正在hi傳遞一個蘋果,不是嗎?但是呼叫a.hi(a)是呼叫名為的方法hi(Lfruit;)V(因為型別a是水果)。接收變數 ( a) 具有編譯時型別Fruit,但它的實際型別是Apple. 所以,hi(Lfruit;)V選擇哪種方法是通過動態調度完成的——你得到了蘋果的hi(Fruit). 僅由編譯器決定是否使用hi(Fruit)和。假設運算式的型別是 Fruit,你會得到. 如果你決議這個運算式,你會得到一個物件,它的return不會改變這一點。hi(Apple) ahi(Fruit).getClass()Apple.class
因此,您寫的內容不會覆寫。不同的名字,即使你抹去。
將泛型混入其中,它會變得很時髦。但你可以做到這一點。
public abstract class ApplicationDTOManager<I extends CreationRequest, O extends CreationResponse> {
public abstract static class CreationRequest {}
public abstract static class CreationResponse {}
}
public abstract class ApplicationController<
E extends ApplicationEntity,
S extends ApplicationService<AppEntity>,
I extends CreationRequest,
O extends CreationResponse,
M extends ApplicationDTOManager<I, O>
>
// heck that's probably too many, at some point generics aren't worth it
{
public abstract boolean hasCreatePermissions(I requestBody);
}
@RestController
public class UserResource extends ApplicationController<
User,
// UserService<User>, // this seems wrong!
UserService, // UserService should extends ApplicationService<User>
UserDTOManager.CreationRequest,
UserDTOManager.CreationResponse,
UserDTOManager> {
@Override
public boolean hasCreatePermissions(UserDTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
// Stuff
}
}
不確定這一切是否值得痛苦,但是,如果您堅持將所有這些與泛型聯系在一起,那么以上是唯一的方法。您不能表達“具有作為 CreationRequest 子型別的內部型別”的概念是泛型系結。
uj5u.com熱心網友回復:
如果您覆寫一個方法,則不能有不同的簽名,如果您覆寫的方法需要 DTOManager.CreateRequest,則您不能在覆寫方法中使用子類。
您必須“支持”父方法可以采用的所有型別的輸入。
我相信您需要這個,因為權限驗證依賴于子類的方法或欄位。如果是這樣,您應該在子類中實作它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/523367.html
標籤:爪哇弹簧靴仿制药
