15.Solidity继承
继承可以理解为一个合约从另一个合约获取合约结构(除私有状态变量和私有函数)的过程,目的是为了实现代码复用,被继承者被称为基类合约或父合约,继承者被称为派生合约或子合约。
子合约继承父合约时,在区块链上只有一个合约被创建,父合约的合约结构(除私有状态变量和私有函数)被编译到子合约中,可以理解为在父合约中编写的合约结构(除私有状态变量和私有函数)复制到子合约中后子合约再部署到区块链上。
合约继承声明格式如下:
contract ContractName is <parent contract list>{
// 语句
}
字段 |
描述 |
contract |
声明合约的关键字。 |
ContractName |
自定义的合约名称。 |
is |
继承关键字。 |
<parent contract list> |
Solidity语言支持子合约同时继承多个父合约。<parent contract list>是指父合约名称列表,父合约名称间用逗号隔开。 |
// 声明父合约
contract ParentContract{
// 声明状态变量
string str = unicode"状态变量被调用";
// 声明函数
function fun() public pure returns(string memory){
return unicode"函数被调用";
}
// 声明函数修改器
modifier CheckNum(){
require(false, unicode"函数修改器被调用");
_;
}
// 声明事件
event Log(string);
// 声明自定义Error错误
error err(string);
// 声明结构体
struct Student{
string name;
uint age;
}
// 声明状态类型
enum Stauts{unpaid, shipped, received, evaluted}
}
// 声明子合约并继承ParentContract合约
contract ChildrenContract is ParentContract{
// 调用在父合约中声明的状态变量
function aCallVar() public view returns(string memory){
return str;
}
// 调用在父合约中声明的函数
function bCallFun() public pure returns(string memory){
return fun();
}
// 调用在父合约中声明的函数修改器
function cCallModifier() public pure CheckNum(){
}
// 调用在父合约中声明的事件
function dCallEvent() public{
emit Log(unicode"事件被调用");
}
// 调用在父合约中声明的自定义Error错误
function eCallError() public pure{
revert err(unicode"自定义Error错误被调用");
}
// 调用在父合约中声明的结构体
function fCallStruct() public pure returns(Student memory){
Student memory stu;
stu.name = "Alice";
stu.age = 20;
return stu;
}
// 调用在父合约中声明的枚举类型
function gCallEnum() public pure returns(Stauts){
return Stauts.received;
}
}
15.1.重写
重写是指子合约对父合约的合约结构进行重新编写,目的为了在父合约的合约结构无法满足需求时,子合约需要对这个合约结构进行改良来满足新的需求。Solidity语言支持重写的合约结构只有函数和函数修改器。
允许被重写的合约结构需要被virtual关键字修饰;要重写父合约-合约结构的子合约-合约机构需要被override关键字修饰。
15.1.1.重写函数
- 重写函数规则
- 重写函数与被重写函数的名称要相同
- 重写函数与被重写函数的输入参数类型和数量要相同
- 重写函数与被重写函数的返回参数类型和数量要相同
// 声明父合约
contract Parent{
// 声明无参被重写函数,被重写函数需要用virtual关键字修饰
function test() public pure virtual returns(string memory){
return unicode"父合约";
}
// 声明有参被重写函数,被重写函数需要用virtual关键字修饰
function test(string memory _str) public pure virtual returns(string memory){
// string.concat():将多个字符串组合成一个字符串
string memory str = string.concat(unicode"父合约的输入参数为:", _str);
return str;
}
}
// 声明子合约
contract Children is Parent{
// 声明无参重写函数,重写函数需要用override关键字修饰
// 父合约和子合约的test函数都无输入参数,返回参数都为string类型,则子合约的无参test函数会重写父合约的无参test函数
function test() public pure override returns(string memory){
return unicode"子合约";
}
// 声明有参重写函数,重写函数需要用override关键字修饰
// 父合约和子合约的test函数的输入参数都为string类型,返回参数都为string类型,则子合约的有参test函数会重写父合约的有参test函数
function test(string memory _str) public pure override returns(string memory){
// string.concat():将多个字符串组合成一个字符串
string memory str = string.concat(unicode"子合约的输入参数为:", _str);
return str;
}
}
- 函数可见性对重写的影响
- 可见性为public的被重写函数,只可被public函数重写
- 可见性为external的被重写函数,可被public或external函数重写
- 可见性为internal的被重写函数,只可被internal函数重写
- 可见性为private的函数不可同时被virtual关键字修饰,因此private函数不可被重写
// 声明父合约
contract Parent{
// 声明可见性为public的被重写函数
function pubFun() public pure virtual returns(string memory){
return "parent: public pubFun";
}
// 声明可见性为external的被重写函数
function extFun() external pure virtual returns(string memory){
return "parent: external extFun";
}
// 声明可见性为internal的被重写函数
function intFun() internal pure virtual returns(string memory){
return "parent: internal intFun";
}
// private函数不可同时使用virtual关键字
// function priFun() private pure virtual returns(string memory){
// return "parent: private priFun";
// }
}
// 声明子合约
contract Children is Parent{
// 可见性为public的pubFun函数重写父合约的pubFun函数
function pubFun() public pure override returns(string memory){
return "children: public pubFun";
}
// 可见性为public的extFun函数重写父合约的extFun函数
function extFun() public pure override returns(string memory){
return "children: public extFun";
}
// 可见性为external的extFun函数重写父合约的extFun函数
// function extFun() external pure override returns(string memory){
// return "children: external extFun";
// }
// 可见性为internal的intFun函数重写父合约的intFun函数
function intFun() internal pure override returns(string memory){
return "children: internal intFun";
}
}
- 函数状态可变性对重写的影响
- 状态可变性为pure的被重写函数,只可被pure函数重写
- 状态可变性为view的被重写函数,可被pure或view函数重写
- 状态可变性为nonpayable的被重写函数,可被pure、view或nonpayable函数重写
- 状态可变性为payable的被重写函数,只可被payable函数重写
// 声明父合约
contract Parent{
// 声明状态可变性为pure的被重写函数
function pureFun() public pure virtual returns(string memory){
return "parent: pure pureFun";
}
// 声明状态可变性为view的被重写函数
function viewFun() public view virtual returns(string memory){
return "parent: view viewFun";
}
// 声明状态可变性为nonpayable的被重写函数
function nonpayableFun() public virtual returns(string memory){
return "parent: nonpayable nonpayableFun";
}
// 声明状态可变性为payable的被重写函数
function payableFun() public payable virtual returns(string memory){
return "parent: payable payableFun";
}
}
// 声明子合约
contract Children is Parent{
// 状态可变性为pure的pureFun函数重写父合约的pureFun函数
function pureFun() public pure override returns(string memory){
return "children: pure pureFun";
}
// 状态可变性为pure的viewFun函数重写父合约的viewFun函数
function viewFun() public pure override returns(string memory){
return "children: pure viewFun";
}
// 状态可变性为view的viewFun函数重写父合约的viewFun函数
// function viewFun() public view override returns(string memory){
// return "children: view viewFun";
// }
// 状态可变性为pure的nonpayableFun函数重写父合约的nonpayableFun函数
function nonpayableFun() public pure override returns(string memory){
return "children: pure nonpayableFun";
}
// 状态可变性为view的nonpayableFun函数重写父合约的nonpayableFun函数
// function nonpayableFun() public view override returns(string memory){
// return "children: view nonpayableFun";
// }
// 状态可变性为nonpayable的nonpayableFun函数重写父合约的nonpayableFun函数
// function nonpayableFun() public override returns(string memory){
// return "children: nonpayable nonpayableFun";
// }
// 状态可变性为payable的payableFun函数重写父合约的payableFun函数
function payableFun() public payable override returns(string memory){
return "children: payable payableFun";