<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      謹(jǐn)慎使用Marker Interface

        之所以寫這篇文章,源自于組內(nèi)的一些技術(shù)討論。實(shí)際上,Effective Java的Item 37已經(jīng)詳細(xì)地討論了Marker Interface。但是從整個(gè)Item的角度來(lái)看,其對(duì)于Marker Interface所提供的一系列優(yōu)點(diǎn)及特殊特性實(shí)際上是持肯定態(tài)度的。因此很多人,包括我的同事,都將該條目中的一些結(jié)論當(dāng)作是準(zhǔn)則來(lái)去執(zhí)行,卻忽略了得到這些結(jié)論時(shí)的前提,進(jìn)而導(dǎo)致了一定程度的誤用。

        當(dāng)然,我并不是在反對(duì)Effective Java的Item 37。說(shuō)實(shí)話,我也沒(méi)有這個(gè)資本。只是我個(gè)人在技術(shù)上略顯保守,因此希望通過(guò)這篇文章闡述一下Marker Interface可能帶來(lái)的一系列問(wèn)題,進(jìn)而使大家更為謹(jǐn)慎而且準(zhǔn)確地使用Marker Interface。

       

      Marker Interface簡(jiǎn)介

        或許有些讀者并不了解什么是Marker Interface。那么首先讓我們來(lái)看看JDK中Set接口的實(shí)現(xiàn):

      1 public interface Set<E> extends Collection<E> {
      2 }

        細(xì)心的讀者會(huì)發(fā)現(xiàn),實(shí)際上Set較Collection沒(méi)有添加任何接口函數(shù)。那為什么JDK還要為其定義一個(gè)額外的接口呢?

        相信您很快就能答出來(lái):“這是因?yàn)镾et中所包含的數(shù)據(jù)中不會(huì)有重復(fù)的元素,而Collection接口作為集合類型接口的根接口,其沒(méi)有添加這種限制。”

        是的。JDK提供一個(gè)額外的Set接口的確就是出于這個(gè)目的。而且這種不添加任何新成員的接口實(shí)際上就是Marker Interface。而且在JDK中,Marker Interface還不少。另一個(gè)非常著名的Marker Interface就是Clonable接口:

      1 public interface Cloneable {
      2 }

        只是這一次,Marker Interface所受到的禮遇并不相同:無(wú)論是在對(duì)Prototype模式的講解中還是在其它日常討論中,其都是作為反面教材來(lái)詮釋什么是一個(gè)不良的設(shè)計(jì)。

       

      硬幣的正反面

        那Marker Interface到底是好還是不好呢?如果沒(méi)有分析,我們就不會(huì)知道為什么Marker Interface在不同的情況下得到如此不同的評(píng)價(jià),也更不會(huì)知道如何正確地使用Marker Interface。因此我們先不說(shuō)結(jié)論,而是從接口Set及Clonable兩個(gè)截然不同的情況來(lái)分析Marker Interface表現(xiàn)出如此差異的原因。

        正能量先行。我們先來(lái)分析Set這個(gè)Marker Interface表現(xiàn)良好的原因。當(dāng)用戶看到Set這個(gè)接口的時(shí)候,他首先想到的就是它是一個(gè)集合,而且該集合具有不會(huì)存在重復(fù)元素這樣一個(gè)性質(zhì)。在對(duì)該接口實(shí)例進(jìn)行操作的時(shí)候,軟件開(kāi)發(fā)人員可以直接通過(guò)調(diào)用Set接口所繼承過(guò)來(lái)的各個(gè)成員函數(shù)來(lái)操作它。這些接口所定義的操作需要由Set接口的實(shí)現(xiàn)類來(lái)定義。因此Set的這種不存在重復(fù)元素的性質(zhì)實(shí)際上是由接口的實(shí)現(xiàn)類所保證的。如在添加一個(gè)元素的時(shí)候,我們不必?fù)?dān)心當(dāng)前是否該元素是否已經(jīng)在集合中存在了:

      1 Set<Item> itemSet =2 itemSet.add(item);

        而對(duì)于其它類型的集合,如List,我們就需要檢查元素是否已經(jīng)在集合中存在,否則其內(nèi)部將存在著對(duì)該元素的重復(fù)引用:

      1 List<Item> itemList =2 if (!itemList.contains(item)) {
      3     itemList.add(item);
      4 }

        反過(guò)來(lái),另一個(gè)Marker Interface Clonable則是臭名昭著的。具體原因已經(jīng)在Effective Java中的Item 17中已經(jīng)講得很清楚了。實(shí)際上,創(chuàng)建該接口的思路和創(chuàng)建Set接口的思路原本是一致的:該接口用來(lái)標(biāo)示實(shí)現(xiàn)了該接口的類型是可以被拷貝的。其中的一個(gè)問(wèn)題在于,Object類型的clone()函數(shù)是受保護(hù)的。從而使得用戶代碼不能調(diào)用Clonable接口的clone()函數(shù)。這樣就要求用戶通過(guò)其它方法來(lái)實(shí)現(xiàn)Clonable接口所表示的語(yǔ)義。進(jìn)而在代碼中產(chǎn)生了大量的如下代碼:

      1 if (obj instanceof Clonable) {
      2     ……
      3 } else {
      4     ……
      5 }

        這樣,如果一個(gè)實(shí)例實(shí)現(xiàn)了特定的接口,如Clonable,我們就對(duì)它進(jìn)行特殊的處理。這正是Marker Interface被大量誤用的一種情況:通過(guò)判斷一個(gè)實(shí)例是否實(shí)現(xiàn)了特定Marker Interface來(lái)決定對(duì)其進(jìn)行處理的邏輯。這種對(duì)Marker Interface進(jìn)行使用的代碼實(shí)際上破壞了封裝性:Marker Interface實(shí)例無(wú)法通過(guò)成員函數(shù)等方法控制外部系統(tǒng)對(duì)實(shí)例的使用方式。反過(guò)來(lái),實(shí)現(xiàn)了Marker Interface的類型到底是被如何處理的則是由用戶代碼決定的。而Marker Interface僅僅是建議用戶代碼對(duì)其進(jìn)行操作。也就是說(shuō),Marker Interface擁有了它的使用者相關(guān)的信息,因此其與當(dāng)前系統(tǒng)中的使用者在邏輯上是相互耦合的,從而使得實(shí)現(xiàn)了Marker Interface的類型無(wú)法在其它系統(tǒng)中重用。

        而這也就是Effective Java的Item 37所強(qiáng)調(diào)的:通過(guò)Marker Interface來(lái)定義一個(gè)類型。我們知道,在定義一個(gè)類型的時(shí)候,我們不僅僅需要指定表示該類型所需要的數(shù)據(jù),更為重要的則是為該類型抽象出用于操作該類型的接口。這些接口規(guī)定了該類型的操作方式,從而隔離了該類型的內(nèi)部實(shí)現(xiàn)和用戶代碼。如果我們需要在這些接口之外通過(guò)判斷是否是特定類型來(lái)執(zhí)行特殊的處理,那么也就表示該Marker Interface所定義的類型從語(yǔ)義上來(lái)講是并不合適的。

        而且從上面對(duì)Set接口以及Clonable接口的比較中可以看出,如果就像Effective Java的Item 37一樣通過(guò)Marker Interface來(lái)定義類型,那么對(duì)類型進(jìn)行定義的方式主要分為兩種:從一個(gè)接口派生以使得Marker Interface擁有較父接口多出的特殊性質(zhì)。而如果Marker Interface沒(méi)有一個(gè)父接口,那么其應(yīng)該是Object類所具有的一種特殊性質(zhì),并可以通過(guò)Object類所提供的各個(gè)組成來(lái)按該性質(zhì)進(jìn)行操作,就像Serializable接口那樣。

        從一個(gè)接口派生來(lái)定義Marker Interface是比較常見(jiàn)的情況,但是也較容易出錯(cuò)。一個(gè)比較經(jīng)典的示例仍然是基于長(zhǎng)方形為正方形定義一個(gè)接口。假設(shè)一個(gè)系統(tǒng)中已經(jīng)擁有了一個(gè)用來(lái)表示長(zhǎng)方形的接口:

      1 public interface Rectangle {
      2     void setWidth(double width);
      3     void setHeight(double height);
      4     double getArea();
      5 }

        由于正方形是長(zhǎng)方形的長(zhǎng)和寬都相等的一種特殊情況,因此我們常常認(rèn)為正方形是一種特殊的長(zhǎng)方形。對(duì)于這種情況,軟件開(kāi)發(fā)人員就可能決定通過(guò)從長(zhǎng)方形接口派生來(lái)定義一個(gè)正方形:

      1 public interface Square extends Rectangle {
      2 }

        但是在使用過(guò)程中,他會(huì)別扭得要死。原因就是因?yàn)閷?shí)際上對(duì)長(zhǎng)方形所定義的接口,如setWidth(),setHeight()等對(duì)于正方形而言完全沒(méi)有意義。正方形所需要的是能夠設(shè)置它的邊長(zhǎng)。因此一個(gè)正確定義Marker Interface的前提就是原有接口中的各個(gè)成員對(duì)于Marker Interface所定義的概念仍然具有明確的意義。

        OK,相信您在看到長(zhǎng)方形和正方形這個(gè)示例的時(shí)候首先想到的就是里氏替換原則(Liskov Substitution Principle)。但請(qǐng)不要使用里氏替換原則來(lái)判斷一個(gè)Marker Interface的定義是否合適。這是因?yàn)槔锸咸鎿Q原則實(shí)際上是使用在對(duì)象之間的:如果S是T的子類型,那么S對(duì)象就應(yīng)該能在不改變?nèi)魏纬橄髮傩缘那闆r下替換所有的T對(duì)象。畢竟,無(wú)論如何我們創(chuàng)建的都應(yīng)該是一個(gè)類型的實(shí)例,而不能直接創(chuàng)建接口的實(shí)例(基于匿名類的除外)。

        例如對(duì)于Set接口,如果我們將所有對(duì)Collection接口的使用都替換為對(duì)Set接口的使用,那么至少對(duì)下面的語(yǔ)句進(jìn)行替換時(shí)會(huì)導(dǎo)致編譯器報(bào)出編譯錯(cuò)誤:

      1 Collection<Item> itemCollection = new ArrayList<Item>();

        因此,使用里氏替換原則來(lái)判斷一個(gè)Marker Interface是否合適實(shí)際上真沒(méi)有太多意義,這在stackoverflow上也有頗多討論。

       

      Marker Interface vs. Annotation

        在前面的章節(jié)中已經(jīng)提到過(guò),Marker Interface表示實(shí)現(xiàn)該接口的類型具有特殊的性質(zhì)。也就是說(shuō),Marker Interface是該類型的一個(gè)特性,也即是該類型的一個(gè)元數(shù)據(jù)。而在Java中,另一個(gè)可以用來(lái)表示類型元數(shù)據(jù)的Java組成是標(biāo)記。在處理相似問(wèn)題的情況下,不同的類庫(kù)選擇了不同的解決方案。例如Java中的序列化支持實(shí)際上是通過(guò)Serializable這個(gè)Marker Interface來(lái)完成的:

      1 public class Employee implements java.io.Serializable
      2 {
      3     public String name;
      4     public String address;
      5     public transient int SSN;
      6     public int number;
      7 }

        而在JPA中,用來(lái)對(duì)持久化到數(shù)據(jù)庫(kù)這一功能的控制是通過(guò)標(biāo)記來(lái)完成的:

       1 @Entity
       2 @Table(name = "employee")
       3 public class Employee {
       4     @Column(name = "name", unique = false, nullable = false, length = 40)
       5     private String name;
       6 
       7     @Column(name = "address", unique = false, nullable = false, length = 200)
       8     private String address;
       9 
      10     @Column(name = "number", unique = false, nullable = false)
      11     private int number;
      12 
      13     @Transient
      14     private float percentageProcessed;
      15     ......
      16 }

        隨之而來(lái)的一個(gè)問(wèn)題就是:我們應(yīng)該在什么情況下使用Marker Interface,又在什么情況下使用標(biāo)記呢?了解何時(shí)使用的前提就是了解兩者之間的優(yōu)劣。由于兩者是完全不同的兩種語(yǔ)法結(jié)構(gòu),因此它們之間的區(qū)別就顯得非常明顯:

        首先從Marker Interface說(shuō)起。該方法較標(biāo)記的好處則在于,通過(guò)instanceof就直接能探測(cè)一個(gè)實(shí)例是否是一個(gè)特定接口的實(shí)例,而標(biāo)記則需要通過(guò)反射等方法來(lái)判斷特定實(shí)例上是否有特定的標(biāo)記。除了這個(gè)原因之外,對(duì)一個(gè)實(shí)例是否實(shí)現(xiàn)了某個(gè)接口可以在編譯時(shí)就可以進(jìn)行檢查,而一個(gè)實(shí)例是否有某個(gè)標(biāo)記則在運(yùn)行時(shí)才能進(jìn)行。在使用instanceof的時(shí)候,實(shí)際上我們是在探測(cè)某個(gè)實(shí)例是否是某個(gè)類型。因此對(duì)于Marker Interface來(lái)說(shuō),其首先需要有一定的實(shí)際意義。

        標(biāo)記較Marker Interface的好處則在于:其粒度更細(xì)。可以說(shuō),Marker Interface只能施行在類型上,而標(biāo)記則可以施行在多種類型組成上,因此Marker Interface實(shí)際上是作為整體行為的一種考慮,而標(biāo)記則更注重具體細(xì)節(jié)。一個(gè)定義良好的細(xì)粒度API可以提供更大的靈活性。而且相較于接口,標(biāo)記的后續(xù)發(fā)展能力更強(qiáng),畢竟在一個(gè)接口中添加一個(gè)成員函數(shù)是一個(gè)非常麻煩的事情。

        其實(shí)Marker Interface以及標(biāo)記之間擁有如此大的混淆的很大一部分原因則是兩者在功能上有重復(fù),而且在Java演化過(guò)程中出現(xiàn)的時(shí)機(jī)并不相同,導(dǎo)致在一些地方仍然擁有Marker Interface的不正當(dāng)使用。實(shí)際上,像Clonable這種值得商榷的Marker Interface在JDK中還有很多很多。之所以在JDK里面會(huì)出現(xiàn)那么多的Marker Interface,其中一個(gè)原因也是因?yàn)镴ava對(duì)標(biāo)記的支持比較晚的緣故。

       

      轉(zhuǎn)載請(qǐng)注明原文地址并標(biāo)明轉(zhuǎn)載:http://www.rzrgm.cn/loveis715/p/5094367.html

      商業(yè)轉(zhuǎn)載請(qǐng)事先與我聯(lián)系:silverfox715@sina.com

      posted @ 2016-01-02 11:50  loveis715  閱讀(4225)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 日本高清一区免费中文视频| 精品无码久久久久久尤物| 苗栗市| 国产一区二区不卡在线视频| 99久久精品久久久久久婷婷| 国产一级片内射在线视频| 国产精品天干天干综合网| 草草浮力影院| 亚洲欧美日韩一区在线观看| 亚洲国产成人不卡高清麻豆| 精品亚洲女同一区二区| 日韩人妻无码一区二区三区99| 欧美人成精品网站播放| 中国少妇嫖妓BBWBBW| 国产精品无遮挡猛进猛出| 久久精品波多野结衣| 好男人日本社区www| 吴旗县| 日本欧美大码a在线观看| 亚洲AV国产福利精品在现观看| 国产一区二区不卡在线| 久久人妻国产精品| www成人国产高清内射| 国产亚洲av夜间福利香蕉149| 成a人片亚洲日本久久| 你拍自拍亚洲一区二区三区| av在线播放观看国产| 亚洲人妻精品中文字幕| 欧美高清freexxxx性| 久久被窝亚洲精品爽爽爽| 久久人妻精品国产| 胸大美女又黄的网站| 久久精品国产九一九九九| 高清自拍亚洲精品二区| 三上悠亚久久精品| 亚洲综合国产激情另类一区| 精品 无码 国产观看| 一区二区在线观看 激情| 顺义区| 久久精品一本到99热免费| 免费无码又爽又刺激成人|