大李拍了拍我的肩膀說:“你真有想象力,不過的確,有很多文獻把這 種用Implements來實現接口的方法就稱為接口繼承。其實,接口自己也是可以進 行繼承的,在VB.NET中把接口間的繼承形式稱為接口繼承。”
我不 禁跟著笑了起來:“接口繼承要成為繼承,當然要用Inherits,對吧? ”
大李點點頭說:“既然你都清楚了,那你來模擬一個下拉框 Combobox的接口吧。”
“Combobox?”我不禁一愣,不 過一會就想明白了,“是不是要讓它符合有文本框供文字輸入與下拉列表供 選擇列表項的組合形式這樣的外觀?”
大李跟著提醒了我一句: “接口與VB.NET中的類繼承還是有不同的,它可以支持從多個接口進行多重 繼承,VB.NET中的類只支持單一基類的繼承。”
見大李沒什麼別的 意見,我就開始寫起代碼來:
Interface IControl Sub Paint() End Interface Interface ITextBox Inherits IControl ‘在文本框設置文本 Sub SetText(ByVal text As String) End Interface Interface IListBox Inherits IControl ‘在下拉列表設置列表項 Sub SetItems(ByVal items() As String) End Interface Interface IComboBox Inherits ITextBox, IListBox End Interface Class CHenry Implements IComboBox Sub SetText(ByVal text As String) Implements ITextBox.SetText '實現代碼 End Sub Sub SetItems(ByVal items() As String) Implements IListBox.SetItems '實現代碼 End Sub ……
寫到這,發現CHenry類中的Implements IcomboBox的ICombobox下面還有一條波浪線,說明接口並沒有實現完畢,可是我 已經把IComboBox繼承的兩個基接口中的方法都已經實現了呀。把鼠標靠近波浪線 一看,系統提示“必須為接口IControl實現sub Paint()”,於是我就 繼續寫:
Sub Paint() Implements IControl.Paint '實現代碼 End Sub End Class
我轉回頭問大李:“接口的實現類中是不是要把 接口的所有基接口都要實現一遍呀?”
大李點點頭說:“如果 象這個演練中的情況,當然是要把基接口中沒有實現的方法進行實現。但也要注 意,實現接口的類或結構會隱式地實現該接口的所有基接口。如果一個接口在基 接口的可傳遞閉包中多次出現,它的成員只參與一次構成派生接口。實現派生接 口的類型只需實現一次多次定義的基接口方法。所以你也可以用Sub Paint() Implements ITextbox.Paint或是Sub Paint() Implements IListBox.Paint來代 替,但只能用這三個定義中的一個。你再來看這段代碼。”大李開始修改起 剛寫好的代碼來:
Interface IControl Sub Paint() End Interface Interface ITextBox Inherits IControl ‘在文本框設置文本 Sub SetText(ByVal text As String) Shadows Sub Paint() End Interface Interface IListBox Inherits IControl ‘在下拉列表設置列表項 Sub SetItems(ByVal items() As String) End Interface Interface IComboBox Inherits ITextBox, IListBox End Interface Sub test(ByVal x As IComboBox) x.Paint() End Sub
“這裡的x.Paint()是哪一個接口的方法?IControl 是ITextBox?”大李一臉笑意,真是氣人。但是,我應該可以回答上來的, 我按類的隱藏的概念回憶了一下(詳見前文《重載和隱藏》),哈,明白了,它 當然是調用它直接被派生的那個基類中的方法呀。
“是ITextBox中 的方法吧!”
“可以呀,不錯!”大李簡單地誇了我一 句,然後喝了口水,繼續說:“基接口的成員名稱在繼承分層結構的一條路 徑中被隱藏,但它在其它的路徑中不會被隱藏,比如我們可以從IlistBox中去繼 承Icontrol中的Sub Paint()。”
“可是,在您的這個示例中 的sub test裡,x是接口的實例嗎?可是,接口還沒有實現呀?”我還是有 問題要問。
“test方法其實可以接受任何將 IComboBox 實現為小部 件參數的對象,即使對接口 IComboBox 的實現可能相差很大。”大李回答 道。
“是不是說我們在使用的時候,可以用實現IComboBox接口的類 ,比如CHenry的一個實例去代替x?”
大李笑著說:“基本上 差不多了,你自己慢慢考慮吧。還有個問題比較有意思:實現類中用於實現接口 的方法或屬性名倒不用與接口中定義的名字一樣,只要參數列表與返回類型一致 就行了。比如在CHenry中的sub Paint()如果更名為sub xxx()也是可以的,只要 後面跟著Implements IControl.Paint就行了。命名一定要有規劃,不然,接口繼 承中也會帶來命名重復造成的問題,我們來看一下。”
Interface IHenry1 Property yyy() As Integer End Interface Interface IHenry2 Sub yyy(ByVal i As Integer) End Interface Interface IHenryDerived Inherits IHenry1 Inherits IHenry2 End Interface Sub test(ByVal x As IHenryDerived) x.yyy(1) x.yyy = 10 End Sub
“你看,在sub test()中,無論你按IHenry2中的定 義方式來使用x.yyy(1),還是用IHenry1中的方式來使用x.yyy=10,集成編譯器都 會在它們下方打上波浪線,表示出錯,是什麼錯呢?”大李一邊問我,一邊 把鼠標靠近波浪線,出現了編譯器的出錯提示:
“yyy”在繼 承接口“IHenry1”與“IHenry2”之間不明確
“所以,我一直強調命名規則,對吧?”大李看了我一眼, “其實解決方法倒用不著去更改基接口中的方法與屬性名。 ”
Sub test() Dim x As IHenryDerived CType(x, IHenry1).yyy = 10 CType(x, IHenry2).yyy(1) End Sub
“哦,用強制類型轉換就可以了。”我又學到 一招,不禁暗自竊喜。但是我心裡總是有一個不大不小的疙瘩,說來說去,這接 口與抽象類可真的太象了。趕緊得問問:“大李哥,這接口與 ……”
“抽象類?”大李一口就接了上來 :“別急,小伙子,看看幾點了,該下樓吃午飯了。”