??一個標(biāo)記接口是一個接口,沒有包含方法聲明僅委托(或“標(biāo)記”)一個實現(xiàn)該接口的類具有某些屬性。比如,考慮Serializable接口(第12章).通過實現(xiàn)這個接口,一個類指示了它的實例可以被寫為一個ObjectOutputStream(或“序列化的”)。
??你可能會聽說標(biāo)記注解( item39 )導(dǎo)致標(biāo)記接口過時了。這個斷言是不正確的。標(biāo)記接口比標(biāo)記注解有兩個優(yōu)勢。首先也是最重要的, 標(biāo)記接口定義由標(biāo)記類的實例實現(xiàn)的類型,標(biāo)記注解不能。標(biāo)記類下的存在允許你在編譯時間捕獲錯誤,如果你使用一個標(biāo)記注解,在運行時間之前不能捕獲異常。
??Java的序列化設(shè)施(第六章)使用Serializable標(biāo)記接口來指示一個類型是可序列化的。ObjectOutputStream.writeObject方法,用來傳遞序列化的對象,需要它的參數(shù)是被序列化的。如果該方法的參數(shù)擁有Serializable類型,在編譯時(通過類型檢查)會檢測到序列化了不合適的對象。編譯時間錯誤的檢測是標(biāo)記接口的意圖,但是不幸的是, ObjectOutputStream.write API并沒有利用Serializable接口的優(yōu)勢:它的參數(shù)被聲明為Object類型,所以嘗試序列化一個未序列化的對象在運行時間并不會失敗。
??另一個標(biāo)記接口比標(biāo)記注解的好的地方是它們定位更精確。如果一個注解類型通過目標(biāo)ElementType.TYPE聲明,它可以應(yīng)用于任何類或接口。假設(shè)你有一個標(biāo)記只適用于特定接口的實現(xiàn)。如果你定義它作為標(biāo)記接口,你可以讓他繼承其適用的僅有的接口,確保所有標(biāo)記類型也都是其適用的唯一接口的子類型。
??可以說,Set接口就只是一個 受限制的標(biāo)記接口。它只對Collection的子類型適用,但是除了通過Collection定義的方法,它沒有添加任何方法。它通常不被認(rèn)為是標(biāo)記接口因為它改善了一些Collection方法的契約,包括add,equals,hashCode.但是很容易想象一個標(biāo)記接口只適用于一些特定接口的子類型,并沒有改善任何接口的方法。這樣的標(biāo)記接口可能描述了整個對象的一些不變量或指示實例夠資格來處理一些其他類的方法(通過Serializable接口指示實例符合ObjectOutputStream處理的條件)
??標(biāo)記注解比標(biāo)記接口的主要優(yōu)勢是它們是更大的注解工具的一部分。因此,標(biāo)記注解允許在基于注解的框架中保持一致性。
??那么我們什么時候應(yīng)該用標(biāo)記注解什么時候應(yīng)該用標(biāo)記接口呢?如果標(biāo)記應(yīng)用于任何程序元素而不是類或接口時,明顯你必須使用一個注解,因為只有類和接口能夠?qū)崿F(xiàn)或繼承接口。如果只允許標(biāo)記類和接口,問你自己一個問題:我想要編寫一個或更多只接受這個標(biāo)記的對象的方法嘛?如果是,你應(yīng)該使用一個標(biāo)記接口而不是標(biāo)記注解。這將使您能夠?qū)⒔涌谟米飨嚓P(guān)方法的參數(shù)類型,這將帶來編譯時類型檢查的好處。如果你可以說服自己你將永遠(yuǎn)不會想要編寫一個方法只接受標(biāo)記的對象,你大概更希望使用標(biāo)記注解。此外,如果標(biāo)記是大量使用注解的框架的一部分,那么標(biāo)記注解就是明顯的選擇。
??總之,標(biāo)記接口和標(biāo)記注解都有它們的使用場景。如果你想要定義一個沒有任何新方法關(guān)聯(lián)的類型,標(biāo)記接口才是解決之道。如果你想要標(biāo)記程序元素而不是類和接口或適應(yīng)在框架中的標(biāo)記,并早已有了注解類型的大量使用,標(biāo)記注解就是正確的選擇。 如果您發(fā)現(xiàn)自己正在編寫目標(biāo)為ElementType.TYPE的標(biāo)記注解類型,花點時間弄清楚它究竟應(yīng)該是注解類型,還是標(biāo)記接口更合適。
??某種意義上說,這個item是Item22( item22 )的倒序,”如果你不想要定義一個類型,不要使用一個接口“,最接近的說法是,”如果你確實想要定義一個類型,使用接口“。
本文寫于2019.7.10,歷時2天