はじめに
Solidityは多重継承がサポートされています。当然、ダイヤモンド継承問題も発生します。今まではC#をはじめとした単一継承の言語しか扱ってこなかったので、備忘録がてら多重継承した際の動作をメモしておきます。なお、本記事はethereumやsolidity、継承の仕組みについては知っていることを前提にしているので、関連事項の説明は割愛します。
多重継承の解決
pragma solidity ^0.4.11;
contract Parent {
event Log(string);
function foo() public returns (string) {
emit Log("Parent");
}
}
contract Child1 is Parent {
function foo() public returns (string) {
emit Log("Child1");
Parent.foo();
}
}
contract Child2 is Parent {
function foo() public returns (string) {
emit Log("Child2");
Parent.foo();
}
}
contract GrandSon is Child1, Child2 {}
Solidityは、isキーワードを使用して継承を行います。上記のサンプルコードでは、Parentコントラクトを基底として、Child1とChild2の2つの派生コントラクトを作成しています。さらに、その2つを多重継承したGrandSonコントラクトがあります。注目していただきたいには、Parentで定義されている**fooメソッドはChild1とChild2の両方でオーバーライドされている**点です。
この状態でGrandSonのfooメソッドを呼び出すと、実行されるのはChild2とParentのfooメソッドだけです。つまり、child1のfooメソッドは実行されません。これは、GrandSonが基本的にはChild1を知らないからです。GrandSonからChild1とChild2両方のfoo`メソッドを呼び出すためには、以下のように書き換えます。
pragma solidity ^0.4.11;
contract Parent {
event Log(string);
function foo() public returns (string) {
emit Log("Parent");
}
}
contract Child1 is Parent {
function foo() public returns (string) {
emit Log("Child1");
super.foo(); // superキーワード
}
}
contract Child2 is Parent {
function foo() public returns (string) {
emit Log("Child2");
super.foo(); // superキーワード
}
}
contract GrandSon is Child1, Child2 {}
先ほどのコードと異なり、Child1とChild2内でParentのfooメソッドの呼び出しにsuperキーワードが使っています。superキーワードは、基底クラスを呼び出すためのキーワードです。それだけでなく、GrandSonコントラクトが継承している次の基底コントラクトも探索します。つまり、Child1のfooメソッドも実行されます。GrandSonのfooメソッドを呼び出すと、最終的にはChild1とChild2、Parentのfooメソッドが呼び出されます。
まとめ
Solidityでダイヤモンド継承を行うと、最後に継承したコントラクトだけを辿って実行します。すべての継承ルートを辿りたい場合には、superキーワードを使って基底クラスのメソッドを呼び出す必要があります。