簡體   English   中英

可串行標記接口是否包含默認方法?

[英]can marker interface like serializable contain default methods?

我認為它不能,因為標記接口原則是沒有任何方法,但由於默認方法不是抽象的我不確定。

就Java而言,“標記”接口只是一個常規接口。 因此,它可以像任何(Java-8)接口一樣具有默認方法。

現在,至於這是否違反了Marker界面的原則 ,我不得不說是。 Marker接口應該充當各種標志,僅標識一個類符合某些外部標准。 現在,它可以是Marker接口具有抽象/默認方法,但它將不再純粹滿足定義。

來自Effective Java(第二版)

標記接口是一個不包含方法聲明的接口,但只是指定(或“標記”)一個實現接口具有某些屬性的類。

標記接口是一種設計模式,因此我們可以通過觀察定義是什么來開始回答您的問題:

在早期版本的Java中,標記接口是聲明類的元數據的唯一方法。 例如,Serializable Marker Interface允許類的作者說序列化和反序列化時它們的類將正常運行。

在Java的上下文中,Marker接口的目的是說出關於該類的信息。 有趣的是,它標志着它。 這樣的一個例子是Serializable接口,它什么也沒做,只是標記了一個Class能夠被序列化為一個String 這里的問題是:

定義是否包含功能?

不,我不這么認為。 功能不僅僅是關於類的元數據; 它有助於定義類本身。 從元數據到數據需要一步。 因此,就設計模式而言 ,標記接口無法定義聲明功能; 它可以簡單地聲明實現Class

雖然@Azar的答案是正確的,但我們不能忘記在引入默認方法之前編寫了Effective Java。

什么是標記界面?

有兩種查看標記接口的方法:

  1. 它們是聲明沒有方法的接口。
  2. 它們是不強制執行任何方法的接口。

“官方”定義是第一個,但直到Java 7,這兩個陳述是相同的。 它是Effective Java中的一種重復模式,一旦發布了接口,就無法向其添加任何方法,因為它會強制實現新方法。

但是,這正是默認方法試圖解決的問題:允許接口的演進,而無需改進實現它們的所有類。 它還使上面的兩個語句意味着略有不同:默認方法明顯違反語句1,並且設計不違反語句2。

這在實踐中意味着什么?

想象一下,您編寫了一個XML序列化引擎,並創建了一個標記接口XmlSerializable來配合它:

public interface XmlSerializable {}

到現在為止還挺好。 但是后來你意識到你實際上有一些需要特殊處理的課程,他們需要提供自己的定制轉換器。 所以你可能做的是這樣的:

public interface XmlSerializable {

   public static final Map<Class,Class> CONVERTERS = ...

   default Class customConverter() {
      return CONVERTERS.get(this.getClass());
   }
}

那會阻止XmlSerializable成為標記界面嗎? 你可以說它仍然是一個標記界面,因為你沒有真正直接向界面添加額外的行為,只有影響序列化引擎行為的額外元數據。 另一方面,這個解決方案允許實現類覆蓋customConverter() ,這有點狡猾,標記接口不應該允許。 (再一次, SerializableCloneable依賴於實現類中的“魔術”方法更好嗎?我不這么認為。)

可以說上面的例子不是解決這類問題的好方法,你可能會更好地使用注釋。 但對大多數“真正的”標記界面也是如此。

TL;博士

我們可以得出結論,只有默認方法的接口或多或少等同於空接口。 如果你想進行理論上的區分而不是稱之為標記界面,那當然沒問題。 但是實際上沒什么區別,並且考慮到標記界面的固有問題,我們應該盡量避免它們。

標記接口可以具有默認方法,但具有它們是荒謬的。

標記界面與傳統界面的使用方式不同。 傳統的接口定義了抽象和默認的方法。 因此,程序使用該接口聲明變量作為其類型,並通過該接口類型的引用來調用這兩種類的方法是明智的。

通過約束,標記接口不用於調用方法。 它是關於通過類型系統聲明的對象的元信息。 它通常通過instanceof表達式調用代碼,或偶爾使用Class.isAssignableFrom() 聲明一個類型是標記接口的變量是沒有意義的,因為對這樣的變量沒什么可做的。

JDK中標記接口的示例是CloneableRandomAccessSerializable

現在考慮在某些標記界面中添加默認方法:

interface Marker {
    default void foo() { ... }
}

foo的默認實現可以做什么?

默認方法的實現通常要在操作this ,他們通過調用其它實例方法做this 他們可以調用其他默認方法,但是有一堆默認方法相互調用是沒有用的。 最后,在某種實際操作中this必須執行。 由於接口方法無法訪問狀態(字段),因此任何實際操作都必須由駐留在實現類中的抽象方法實現來執行。 但是,在標記界面中沒有這樣的方法。

foo的默認實現可以在此接口或其他類上調用靜態方法。 這大多沒有意義,因為這樣的方法首先可能更好地表達為靜態方法。 實現可以將this傳遞給靜態方法,但是這個方法對這樣的引用沒有任何用處,因為它沒有方法! 好吧,它可能有默認方法,但現在我們進入圈子。

對於在接口上有用的默認方法,該接口也需要具有抽象方法。 但如果它有抽象方法,它就不再是標記接口。 因此,在標記接口上使用默認方法是沒有意義的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM