請原諒不好的命名,下面描述的問題是對真實代碼的非常粗略的簡化和更改
我定義了以下型別:
interface Bundle {
// omitted
}
interface Content<T extends Bundle> {
void useBundle(T bundle);
Class<? extends T> getSupportedBundleClass();
// omitted
}
class Service<T extends Bundle> {
private final T bundle;
public Collection<Content<? super T>> someMethod(Collection<Object> allContents) {
// omitted
}
}
服務中someMethod的目的是過濾此類Content實體,其支持的捆綁包類與捆綁包的類相同或層次結構更高。
例如:
給定Bundle的層次結構...
interface Bundle {}
interface A extends Bundle {}
class B implements A {}
...而這個設定,...
Content<Bundle> contentBundle = // omitted
Content<A> contentA = // omitted
Content<B> contentB = // omitted
Service<Bundle> serviceBundle = // omitted
Service<A> serviceA = // omitted
Service<B> serviceB = // omitted
List<Object> allContents = List.of(contentBundle, contentA, contentB);
...以下是正確的:
serviceBundle.someMethod(allContents) => [contentBundle]
serviceA.someMethod(allContents) => [contentBundle, contentA]
serviceB.someMethod(allContents) => [contentBundle, contentA, contentB]
以下是someMethod的實作方式(使用輔助方法進行強制轉換和決議通配符):
public Collection<Content<? super T>> someMethod(Collection<Object> allContents) {
return allContents.stream()
.map(obj -> obj instanceof Content ? (Content<? extends Bundle>) obj : null) // cast #1
.filter(Objects:nonNull)
.map(this::castOrNull)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private <U extends Bundle> Content<? super T> castOrNull(Content<U> content) {
return content.getSupportedBundleClass().isInstance(bundle) ? (Content<? super T>) content : null; // cast #2
}
我對castOrNull方法的推理是,如果Service類中的包是提供的Content實體的受支持包類的實體,那么提供的Content的型別引數是T的超型別(bundle的型別)。
someMethod可以正常作業,正如預期的那樣(在上面的示例中描述)。但是,我在注釋**// cast #2**(帶有**// cast #1**的行很好)的Content<U>
行上收到了未經檢查的強制轉換警告。有什么方法可以消除警告(當然,除了抑制它)?Content<? super T>
uj5u.com熱心網友回復:
這種未經檢查的演員表是不可避免的。您正在將不涉及型別變數的東西轉換為有型別變數的東西。為了檢查這個轉換的有效性,JVM 需要知道型別變數T
持有什么型別。不幸的是,它沒有。無論您將Object
轉換為哪種中間型別,您都將在某個時候引入型別變數T
,即當運行時無法檢查轉換的有效性時,轉換將被標記為未經檢查的轉換 - 運行時贏了'不要對這個演員表做任何事情,這可能實際上是無效的,一切都會在以后爆炸。
當然也可以引入自己的check,比如
content.getSupportedBundleClass().isInstance(bundle)
但請注意,這與 JVM所做的檢查非常不同,用于檢查 的有效性(Content<? super T>) content
,如果型別T
在運行時可用。特別是,您的檢查取決于正確Content
實施getSupportedBundleClass
、回傳封閉類而不是封閉類的某些子類的實施者:
class ContentBundle implements Content<Bundle> {
@Override
public void useBundle(Bundle bundle) {
}
@Override
public Class<? extends Bundle> getSupportedBundleClass() {
return B.class;
}
}
getSupportedBundleClass
如果回傳,這將不是問題Class<T>
。
我不知道該欄位是如何bundle
初始化的,所以我不知道這是否會發生,但是您的檢查還取決于Service.bundle
始終是 的實體T
,而不是 的子類T
。如果serviceBundle.bundle
實際存盤 的實體B
,serviceBundle.someMethod(allContents)
則將包括所有 3 個內容。
我建議您使用 aClass<T>
來存盤型別資訊:
private final Class<T> bundleType;
public Service(Class<T> bundleType) {
this.bundleType = bundleType;
}
并使用isAssignableFrom
而不是isInstance
.
如果你認為你自己的檢查就足夠了(例如,如果你確定每個人都會getSupportedBundleClass
按照預期的方式實作),那么你正在做檢查演員的作業,你不應該擔心 JVM 無法檢查它. 壓制它很好。
在大多數情況下,JVM 不檢查強制轉換的解決方案是自己檢查 :)
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/469261.html
上一篇:在Python中使用super()的多級和多重繼承回傳奇數結果
下一篇:如何反向修改道具的值