2012-08-10 36 views
2

對不明的標題感到抱歉。使用空接口實施「通用」代碼中的類型

我讀這本書http://algs4.cs.princeton.edu/home/,我認爲這將是很好的落實圍棋的例子作爲一個學習鍛鍊,但書中使用Java作爲其語言來描述代碼。

一個第一章節討論設置一些核心數據類型/容器樣式類以供稍後重新使用,但我無法嘗試將這些內容嵌入到Go設置中,主要是因爲這些數據類型似乎正在享受Java泛型的使用。

舉例來說,我已經寫了下面的代碼

package bag 

type T interface{} 
type Bag []T 

func (a *Bag) Add(t T) { 
    *a = append(*a, t) 
} 

func (a *Bag) IsEmpty() bool { 
    return len(*a) == 0 
} 

func (a *Bag) Size() int { 
    return len(*a) 
} 

這工作原則上,我可以將項目添加到Bag並檢查它的大小和一切的感覺。然而,這也意味着下面的代碼是合法

a := make(bag.Bag,0,0) 
a.Add(1) 
a.Add("Hello world!") 
a.Add(5.6) 
a.Add(time.Now()) 

我只是想知道是否有強制執行的任何類型的方式,以便它符合類似

Bag<T> bagOfMyType = new Bag<T>() 

例如合同

Bag<Integer> bagOfInts = new Bag<Integer>() 

我知道Go沒有泛型和他們沒有真正的去路,但我只是想知道,如果它是一個可以在編譯的時候「強制執行」任何東西(可能不是)

很抱歉的長期職位

編輯:行,所以我一直在尋找到這一點進一步,我已經差不多事物的仿製藥側(我明白這不是在路線圖放棄去),所以我正在考慮做類似於帶有接口的Haskell類型類的東西,例如

type T interface{} 
type Bag interface { 
    Add(t T) 
    IsEmpty() bool 
    Size() int 
} 

type IntSlice []int 

func (i *IntSlice) Add(t T) { 
    *i = append(*i, t.(int)) // will throw runtime exception if user attempts to add anything other than int 
} 

func (i *IntSlice) IsEmpty() bool { 
    return len(*i) == 0 
} 

func (i *IntSlice) Size() int { 
    return len(*i) 
} 

問題在於這種類型的執行只在運行時執行。

任何人有任何想法如何改善呢?

回答

5

我是新來自己去了,所以我很好奇,如果有人將有一個更好的答案,但這裏是我如何看待它:

你想編譯時強制執行,當Add()被稱爲上IntSlice ,其參數是int。嗯,這裏是你如何做到這一點:

func (i *IntSlice) Add(t int) { 
    *i = append(*i, t) 
} 

由於國內沒有仿製藥,在Add()方法是要對每一種類型的Bag是不同的,所以Bag接口,假設你需要它,就像變成:

type Bag interface { 
    IsEmpty() bool 
    Size() int 
} 

這對我來說很合理,因爲你無法繞過Bag,只是拋出任何東西。知道某件事是Bag是不足以知道如何調用Add();你必須知道你正在處理的是什麼類型的Bag

你可以使接口特定的類型,像IntBag,但由於只有一種類型實際上是要滿足該接口,你可能也擺脫了接口和改變IntSlice名稱IntBag

基本上,這意味着完全依靠任何仿製想放棄了,只是用一些方法你想要什麼做的創建類型:

type IntBag []int 

func (b *IntBag) Add(i int) { 
    *b = append(*b, i) 
} 

func (b IntBag) IsEmpty() bool { 
    return len(b) == 0 
} 

func (b IntBag) Size() int { 
    return len(b) 
} 

更新:有時候真的仿製藥會派上用場。在我看來,我們只能根據具體情況選擇對於特定問題的最佳選擇。用空的接口和反射,你可以得到一些通用的行爲,但它往往是醜陋的,你放棄了一些編譯時類型檢查。或者你放棄泛型,並有一些代碼重複。或者你只是做一個完全不同的方式。

幾周前我問了一個問題,我應該如何使用Go來處理在我看來像他們需要類層次結構的問題。答案基本上是沒有一般解決辦法;這都是個案。我認爲同樣適用於泛型:Go中沒有泛型,沒有將基於泛型的解決方案轉換爲Go的通用解決方案。

在很多情況下,您可能會使用其他語言的泛型,但接口在Go中完全適用(或真正發光)。你的例子是接口不是真正的替代品。另見:Go Vs. Generics

+0

感謝您的答覆。我可以看到的唯一問題是,如果你想在任何帶'''Bag'''接口的函數中使用''''Add'''方法作爲參數(它不起作用),但我想這就是你必須用這個解決方案進行的交易...... – djhworld 2012-08-10 21:10:11

+0

所以,如果你想使它不是通用的,你需要創建一個函數來處理IntBag,這是處理交易的另一個函數用''''''''''''''''''等等,除非您創建某種類型的「調度器」入口點,反映在Bag包上,並根據類型分配呼叫。這仍然意味着編寫'''n'''函數,儘管 – djhworld 2012-08-10 21:21:31

+0

@djhworld我在回答底部添加了更新以迴應您的意見。 – 2012-08-10 22:09:52

1

我對Go非常精通。泛型是一個備受爭議的話題,目前與Java泛型或C++模板沒有任何相似之處。目前的約定是實現一個帶有空接口的「泛型」類型,然後用特定的類型實現包裝它,以確保只使用該類型的元素。以下是Go標準庫中container/list的示例。

http://play.golang.org/p/9w9H1EPHKR

package main 

import (
    "container/list" 
    "fmt" 
) 

type IntList struct { 
    innerList *list.List 
} 

func NewIntList() *IntList { 
    return &IntList{list.New()} 
} 

func (l *IntList) Add(i int) { 
    // this is the only way to add an element to the list, 
    // and the Add() method only takes ints, so only ints 
    // can be added 
    l.innerList.PushBack(i) 
} 

func (l *IntList) Last() int { 
    lastElem := l.innerList.Back() 

    // We can safely type-assert to an int, because Add() 
    // guarantees that we can't put a non-int into our list 
    return lastElem.Value.(int) 
} 

func main() { 
    l := NewIntList() 
    l.Add(5) 
    l.Add(4) 
    l.Add(3) 
    l.Add(2) 
    l.Add(1) 
    fmt.Println("Expecting 1; got:", l.Last()) 
} 
相關問題