Solidity 透過複製程式碼和多型 (polymorphism),來支援多重繼承。
當一個合約繼承多個合約,實際上只會有一個合約被創建,合約會將所有繼承的合約的程式碼,複製到自己身上。
關於 Solidity 對於繼承的設計與 Python
非常相似,尤其多重繼承的部分。
下面的範例,有更詳細的解釋。
pragma solidity ^0.4.22;
contract owned {
constructor() { owner = msg.sender; }
address owner;
}
// 使用 `is` 關鍵字來繼承另一個合約,這樣就可以不需要透過 `this`,也可以存取所有非私有的狀態變數和函示。
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}
// 抽象合約,僅定義函示,但沒有實作函示。如果合約沒有實作所有的函示,那就只能使用介面 (interface)。
contract Config {
function lookup(uint id) public returns (address adr);
}
contract NameReg {
function register(bytes32 name) public;
function unregister() public;
}
// 可以使用多重繼承。雖然 named 合約跟 mortal 合約都有繼承 owned 合約,但實際 named 合約只有一個 owned 合約的實例 (instance),與 C++ 的虛擬繼承 (virtual inheritance) 原理一樣。
contract named is owned, mortal {
constructor(bytes32 name) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
}
// 函示可以被另一個相同名字且相同參數數量的函示覆蓋 (override),如果 override 的函示有不同的回傳值型態會發生錯誤。
function kill() public {
if (msg.sender == owner) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).unregister();
// 可以用這個方式呼叫特定的 overridden 函示
mortal.kill();
}
}
}
// 如果建構子接受傳入參數,則須要在合約的最前頭指定參數的值。
contract PriceFeed is owned, mortal, named("GoldFeed") {
function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo;
}
function get() public view returns(uint r) { return info; }
uint info;
}
當合約被標註為抽象合約時,代表的是至少有一個函示為實現,下面範例無法編譯:
pragma solidity ^0.4.0;
contract Feline {
function utterance() public returns (bytes32);
}
修正後,如下所示:
pragma solidity ^0.4.0;
contract Feline {
function utterance() public returns (bytes32);
}
contract Cat is Feline {
function utterance() public returns (bytes32) { return "miaow"; }
}
如果合約繼承抽象合約後,並沒有實現所有未實現的函示,那麼自己也會是一個抽象合約。
注意:沒有實現的函示與 Function Type 不同,即使它們的語法看起來很相似。
沒有實現的函示範例 (a function declaration):
function foo(address) external returns (address);
函示類型 (Function Type) 的範例:
function(address) external returns (address) foo;
抽象合約將合約定義和合約的實現解耦 (decouple),提供更好的擴充性、自我描述 (self-documentation) 或像 Template method 模式一樣,或減少重複的程式碼。
抽象合約和介面在定義方法的時候非常好用,這樣的設計就像是告訴所有繼承它的子合約,它應該要實作哪些方法 (method)。