第5回 構造体の便利な用途、インターフェイス入門
赤坂 けい
チームWordProgress
2010/3/10
突然登場した新しいプログラミング言語「Go」。その独自性、魅力を余すところなく堪能してみよう(編集部)
今回は、Goのインターフェイスについて学習していく。これは、並列処理を行うgoroutineと並んでGoの特徴的な機能である(goroutineについては第3回「ハロー、goroutine!」を参照してほしい)。
インターフェイスと聞くと、Javaのinterfaceを思い浮かべる人が多いだろう。Goの公式サイトでも、Goのインターフェイス機能は、Javaと類似していると述べられている。また、javaと同様のinterfaceキーワードも採用されている。
しかし、Javaの継承機能を廃しているGoのインターフェイスは、より強力であり、動的言語と同様のダックタイピング(duck typing)が実現可能である。
では、さっそく、Goのインターフェイスを学んでいこう。
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キーワードを用いない例によって、その意味を確認していこう。
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)を見てほしい。
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 |
![]() |
Index | |
構造体の便利な用途、インターフェイス入門 | |
![]() |
Page1 Goのインターフェイスの特徴 構造体の便利な定義とダックタイピング |
Page2 型の異なるオブジェクトの連結 初めてのインターフェイス・プログラム |
![]() |
新世代の並列処理言語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の世界を体験してみよう |

- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
![]() |
|
|
|
![]() |