Solidity 合约继承
Solidity 语言是一种面向对象的编程语言,提供了对合约继承的支持,继承是扩展合约功能的一种方式。
Solidity 语言的合约继承通过关键字 is 来实现。
1. 继承示例
继承通过关键字 is 来实现,例如:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Person{
string public name;
uint public age;
function getSalary() external pure returns(uint){
return 0;
}
}
contract Employee is Person{
}
合约 Employee 继承了合约 Person,运行后,我们看到 Employee 继承了状态变量 name、age 和方法 getSalary。
2. virtual 和 override
solidity 引入了 virtual,override 关键字,用于重写函数。
父合约可以使用 virtual 关键字声明一个虚函数,子合约使用 override 关键字来覆盖父合约的方法,例如:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Person{
string public name;
uint public age;
function getSalary() external pure virtual returns(uint){
return "unkown";
}
}
contract Employee is Person{
function getSalary() external pure override returns(uint){
return 3000;
}
}
子合约 Employee 的 uint 方法覆盖了父合约 Person 的 getSalary 方法,调用子合约 Employee getSalary 方法,输出结果为 3000。
如果合约 Manager 又继承了 Employee,而且还需要覆盖 getSalary 方法,那么需要如下写法:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Person{
string public name;
uint public age;
function getSalary() external pure virtual returns(uint){
return 0;
}
}
contract Employee is Person{
function getSalary() external pure virtual override returns(uint){
return 3000;
}
}
contract getSalary is Employee{
function getSex() external pure override returns(uint){
return 20000;
}
}
3. abstract
solidity 还允许在基类中只声明函数原型,没有实现,而在派生类中再去实现。
solidity 使用 abstract 关键字来标记基类。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract Employee {
function getSalary() public pure virtual returns(int);
}
contract Manager is Employee {
function getSalary() public pure override returns(int){
return 20000;
}
}
抽象合约 abstract 的作用是将函数定义和具体实现分离,从而实现解耦、可拓展性,其使用规则为:
- 当合约中有未实现的函数时,则合约必须修饰为abstract;
- 当合约继承的base合约中有构造函数,但是当前合约并没有对其进行传参时,则必须修饰为abstract;
- abstract合约中未实现的函数必须在子合约中实现,即所有在abstract中定义的函数都必须有实现;
- abstract合约不能单独部署,必须被继承后才能部署;
抽象合约示例 abstract
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
abstract contract Animal {
string public species;
constructor(string memory _base) {
species = _base;
}
}
abstract contract Feline {
uint public num;
function utterance() public pure virtual returns (bytes32);
function base(uint _num) public returns(uint, string memory) {
num = _num;
return (num, "hello world!");
}
}
// 由于Animal中的构造函数没有进行初始化,所以必须修饰为abstract
abstract contract Cat1 is Feline, Animal {
function utterance() public pure override returns (bytes32) { return "miaow"; }
}
contract Cat2 is Feline, Animal("Animal") {
function utterance() public pure override returns (bytes32) { return "miaow"; }
}
4. 子类访问父类权限
子类访问父类权限修饰符包括:public、internal、private,例如:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A{
uint stateVar;
function somePublicFun() public{}
function someInternalFun() internal{}
function somePrivateFun() private{}
}
contract B is A{
function call(){
//访问父类的`public`方法
somePublicFun();
//访问父类的状态变量(状态变量默认是internal权限)
stateVar = 10;
//访问父类的`internal`方法
someInternalFun();
//不能访问`private`
//somePrivateFun();
}
}
5. 传参数到父类
子类传参数到父类有两种方式:
直接传递
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Base{
uint a;
constructor(uint _a){
a = _a;
}
}
contract Derive is Base(1){
function getBasePara() external view returns(uint){
return a;
}
}
根据输入值传递
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Base{
uint a;
constructor(uint _a){
a = _a;
}
}
contract T is Base{
constructor(uint _a) Base(_a) {
}
function getBasePara() external view returns (uint){
return a;
}
}
4. 多重继承中的重名
多重继承中不允许出现相同的函数名、事件名、修改器名以及状态变量名等。
示例以下:
pragma solidity ^0.8.0;
contract Employee1 {
function getSalary() public pure returns(int){
return 1;
}
}
contract Employee2 {
function getSalary() public pure returns(int){
return 1;
}
}
contract Manager is Employee1,Employee2 {
}
由于基类 Employee1、Employee2 中同时包含函数 getSalary,构成重名,所以以上代码会出现编译错误。
pragma solidity ^0.8.0;
contract Employee {
function getSalary() public pure returns(int){
return 1;
}
}
contract Manager is Employee {
function getSalary() public pure returns(int){
return 2;
}
}
由于基类 Employee 和 父类 Manager 中同时包含函数 getSalary,构成重名,所以以上代码会出现编译错误。
还有一种比较隐蔽的情况,默认状态变量的getter函数导致的重名。
示例以下:
pragma solidity ^0.8.0;
contract Employee1 {
uint public data = 10;
}
contract Employee2 {
function data() returns(uint){
return 1;
}
}
contract Manager is Employee1, Employee2{}
由于 Employee1 的状态变量 data,会默认生成 getter,函数名为 data(),于是 Employee1 和 Employee2 函数重名出错。
下一章:Solidity 多重继承
Solidity 语言提供了对合约继承的支持,而且支持多重继承。Solidity 语言的多重继承采用线性继承方式。继承顺序很重要,判断顺序的一个简单规则是按照“最类似基类”到“ ...
AI 中文社