新世代の並列処理言語Google Goをひもとく

第5回 構造体の便利な用途、インターフェイス入門

赤坂 けい
チームWordProgress

2010/3/10

突然登場した新しいプログラミング言語「Go」。その独自性、魅力を余すところなく堪能してみよう(編集部)

 今回は、Goのインターフェイスについて学習していく。これは、並列処理を行うgoroutineと並んでGoの特徴的な機能である(goroutineについては第3回「ハロー、goroutine!」を参照してほしい)。

 インターフェイスと聞くと、Javaのinterfaceを思い浮かべる人が多いだろう。Goの公式サイトでも、Goのインターフェイス機能は、Javaと類似していると述べられている。また、javaと同様のinterfaceキーワードも採用されている。

 しかし、Javaの継承機能を廃しているGoのインターフェイスは、より強力であり、動的言語と同様のダックタイピング(duck typing)が実現可能である。

 では、さっそく、Goのインターフェイスを学んでいこう。

CやJavaなどの静的コンパイル言語に慣れ親しんでいる方は、ダックタイピングというタームに馴染みがないかもしれない。だが、適切な例とともに学べば、ダックタイピングは容易に理解可能なものであるため安心してほしい。

Goのインターフェイスの特徴

 Javaなどと同じく、Goでは、インターフェイスによって、オブジェクト指向プログラミングの多態性(ポリモルフィズム)が実現される。インターフェイスについて、以下の(Java風の)擬似コードで確認しておこう。

abstract class A0 { A0固有メソッドの定義 }
abstract class B0 { B0に固有のメソッド }
interface C0 { C0固有メソッドの定義 }

class A1 extends A0 implements C0 {
    C0固有メソッドの実装
    A0固有メソッドの実装
    A1固有のメソッドの実装(privateメソッドなど)
}
class B1 extends B0 implements C0 {
    C0固有メソッドの実装
    B0固有メソッドの実装
    B1固有のメソッドの実装(privateメソッドなど)
}

 この場合、オブジェクトは、具象クラスのA1とB1から生成される。生成されたオブジェクトは、A1とB1それぞれに固有のメソッドに加えて、インターフェイスC0で定義されたメソッドも持つ。したがって、インターフェイスC0のメソッド定義に従って、双方のオブジェクトのメソッドを横断的に呼び出せる。

 Goのインターフェイスも、上記のような横断的なメソッド呼び出しに用いることができる。加えて、Goはさらに一歩先を行っており、明示的なインターフェイス宣言がなくとも、メソッドのインターフェイスを自動的に生成させることができる。これらにより、Rubyなどの動的スクリプト言語で有名となったダックタイピングが、Goにおいても実現される。

構造体の便利な定義とダックタイピング

 ダックタイピングとは何か。ここでは、interfaceキーワードを用いない例によって、その意味を確認していこう。

●exam5-1.go
package main
import ( "fmt" )

// Person型の定義
type Person struct {
    name string;
    mesg string;
}

// PersonをレシーバとするメソッドString()の定義
func (this *Person) String() string {
    return fmt.Sprintf("私の名前は、%sです。%s\n",this.name,this.mesg)
}

//使う側。こちらはシンプル
func main() {
    pobj := &Person{name:"武田珍念",mesg:"得意技は念仏固めです。"};
    println(pobj);
    fmt.Print(pobj);
}
●実行結果
D:\go-win\mysrc\duck>.\8.exe
0x843250
私の名前は、武田珍念です。得意技は念仏固めです。

 このプログラムでは、第4回のプログラムと同様に、構造体(struct)としてPerson型が定義されている。続いて、Person型に所属するメソッドString( )が定義されている。メソッドString( )の返り値は、Person型のプロパティnameおよびmesgを用いて生成した文字列である。文字列の生成にはfmtパッケージのSprintf関数を用いている。Sprintf関数の書式は、以前取り上げたPrintf関数と同じだ(両者は、Cのsprintf関数とprintf関数の関係に対応している)。

 main関数は、3行だけだが、それぞれに注意が必要だ。1行目では、Person型のオブジェクトを定義するとともに、一気に初期化している。

 Personキーワードに続く「波括弧{ …… }」の部分に注目してほしい。この表記は複合リテラルと呼ばれる記法である。ここでは、name:"武田珍念"およびmesg:"得意技は念仏固めです。"というコロン区切りの記述を含む複合リテラルによって、オブジェクトの2つのプロパティnameとmesgが、初期化されている。

 また、Personキーワードの前に付された&は、Personオブジェクトのアドレスを指示する演算子であり、pobjはPerson型のポインタとなる(すなわち、&の意味はCと同じである)。この記法が便利なことは、前回紹介した下記の定義法と比べてみると分かるだろう。

    pobj := new (Person);
    pobj.name ="武田珍念";
    pobj.mesg ="得意技は念仏固めです。";

 main関数の2行目と3行目では、それぞれpobjを引数としてprintln関数、fmt.Print関数を呼び出している。実行結果を見てみると、printlnの方はオブジェクトのアドレスを示しているらしい「0x843250」という文字列が表示され、fmt.Printの方は、String( )メソッドの実行結果らしい「私の名前は……」という文字列が表示されている。pobjがPerson型のポインタであることからすると、println関数の振る舞いが自然であろう。

 では、fmt.Print関数が、pobjオブジェクトのString( )メソッドを呼び出しているのは、どのようなメカニズムによるのだろうか。

  このメカニズムの中心には、fmt.Print関数が引数として受け取るオブジェクトに対し、string型の値を返すString( )メソッドを有していることへの期待がある。すなわち、fmt.Print(X)という呼び出しがされた場合、fmt.Print関数は、オブジェクトXがこの期待に沿ったString( )メソッドを有しているかどうかをチェックする。オブジェクトXが期待どおりの性質を持っていた場合、X.String( )メソッドを呼び出し、その結果が表示される。

 この期待が、Goにおけるダックタイピング(≒アヒルのように鳴くものはアヒル型とみなす)の特徴を表している。すなわち、fmt.Print関数は、事前にどのような型のオブジェクトが渡されるかを知らないが、そのオブジェクトが妥当なString( )メソッドを有している場合に、そのメソッドを利用する。このことにより、明示的なインターフェイス宣言なしで、多態性(ここでは、それぞれのオブジェクトに適したString( )メソッド呼出し)が実現されている。

 以下、このようなダックタイピングが、静的コンパイル言語であるGoにおいて、いかにして実現されているのかをひもとくために、ダックタイピングを実現する関数を自ら書いていきたい。

 その前に、期待どおりのメソッドがなかった場合は、fmt.Print自身がXの中身をどのように表示するのかも確かめておこう。PersonをレシーバとするメソッドString( )を定義していない例(exam5-2.go)を見てほしい。

●exam5-2.go
package main
import ( "fmt" )

type Person struct {
    name string;
    mesg string;
}

func main() {
    pobj := &Person{name:"武田珍念",mesg:"得意技は念仏固めです。"};
    println(pobj);
    fmt.Print(pobj);
}
●実行結果
D:\go-win\mysrc\duck>.\8.exe
0x323230
&{武田珍念 得意技は念仏固めです。}

 このあたりの挙動が気になる方は、fmt.Print関数やfmt.Printf関数などにいろいろなオブジェクトを入れて、ご自身の手で確かめてみてほしい。

 
1/2
next

Index
構造体の便利な用途、インターフェイス入門
  Page1
Goのインターフェイスの特徴
構造体の便利な定義とダックタイピング
  Page2
型の異なるオブジェクトの連結
初めてのインターフェイス・プログラム

index 新世代の並列処理言語Google Goをひもとく

 Coding Edgeお勧め記事
いまさらアルゴリズムを学ぶ意味
コーディングに役立つ! アルゴリズムの基本(1)
 コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう
Zope 3の魅力に迫る
Zope 3とは何ぞや?(1)
 Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか?
貧弱環境プログラミングのススメ
柴田 淳のコーディング天国
 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く?
Haskellプログラミングの楽しみ方
のんびりHaskell(1)
 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう
ちょっと変わったLisp入門
Gaucheでメタプログラミング(1)
 Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間