はじめに
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
キーワードを使って基底クラスのメソッドを呼び出す必要があります。