当前位置: 首页 > news >正文

solidity中的接口和继承

Solidity 中的接口继承是面向对象编程中非常重要的概念,它们帮助开发者构建模块化、可扩展和可维护的智能合约。


1. 接口

接口可以看作是一份“契约”或“蓝图”。它只声明了外部合约需要实现的函数(包括函数名、参数、返回类型),但不包含任何函数实现

核心特点:
  • 只有声明,没有实现:接口中的函数以 function 关键字开头,直接以分号 ; 结束,没有函数体 {}
  • 隐式抽象:接口本身是隐式抽象的,你不能实例化一个接口(即 new MyInterface 是不行的),你只能通过它来与已实现它的合约进行交互。
  • 继承接口的合约必须实现所有函数:任何合约如果继承(is)了某个接口,就必须实现该接口中声明的所有函数。
  • 限制
    • 所有声明的函数必须是 external 类型。
    • 不能包含构造函数。
    • 不能包含状态变量。
    • 不能包含修饰器。
为什么使用接口?

接口的主要作用是实现合约之间的解耦标准化交互

经典场景: 你写了一个去中心化交易所(DEX)的合约,需要与各种不同的 ERC20 代币合约交互。你不需要知道每个代币合约内部的具体实现,你只需要知道它们都遵循一个标准(比如 ERC20 标准)。这个标准就是通过接口来定义的。

示例:ERC20 接口
// 这是一个简化的 ERC20 接口
interface IERC20 {// 查询余额function balanceOf(address account) external view returns (uint256);// 转账function transfer(address to, uint256 amount) external returns (bool);// 事件event Transfer(address indexed from, address indexed to, uint256 value);
}

现在,你的 DEX 合约就可以通过这个接口与任何 ERC20 代币交互,而无需关心它们的具体实现。

contract MyDEX {// 使用接口类型来声明一个变量IERC20 public token;constructor(address _tokenAddress) {// 将接口类型的变量指向一个具体的合约地址token = IERC20(_tokenAddress);}function getUserBalance(address user) public view returns (uint256) {// 通过接口调用其他合约的函数return token.balanceOf(user);}function sendToken(address to, uint256 amount) public {// 调用代币合约的 transfer 函数require(token.transfer(to, amount), "Transfer failed");}
}

下边代码“contract Counter is ICounter”中,不论是否写了“is ICounter”,都不影响MyContract代码的正常运行。
但的强烈推荐显式使用 is 继承接口。理由如下:

  • 利用编译器进行静态检查:这是最重要的原因。让编译器在编译时帮你发现错误,远比在运行时(在链上)才发现要好得多。
  • 代码即文档:明确表达了设计意图,让代码更容易理解和维护。
  • 安全第一:在智能合约开发中,任何能提前发现错误的手段都应该被采用。
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;interface ICounter {function count() external view returns (uint);function increment() external;
}contract Counter is ICounter{uint public count;function increment() external {count += 1;}
}contract MyContract {ICounter public counter; // 直接存储接口类型constructor(ICounter _counter) {counter = _counter;}function incrementCounter() external {counter.increment(); // 直接调用,无需类型转换}function getCount() external view returns (uint) {return counter.count();}
}

2. 继承

继承允许一个合约(子合约)获取另一个合约(父合约)的所有功能(状态变量和函数),并可以在此基础上进行扩展或修改。这是代码复用的核心机制。

核心特点:
  • 使用 is 关键字:子合约通过 is 关键字来继承父合约。
  • 函数重写:子合约可以重写父合约中的 virtual 函数。重写时必须使用 override 关键字。
  • 调用父合约函数:子合约可以使用 super.functionName() 来调用父合约的函数。
  • 多重继承:Solidity 支持多重继承(一个合约可以继承多个父合约)。
继承的类型和规则:
  1. 简单继承

    contract Ownable {address public owner;constructor() {owner = msg.sender;}modifier onlyOwner() {require(msg.sender == owner, "Not owner");_;}// 这个函数可以被重写,标记为 virtualfunction transferOwnership(address newOwner) public virtual onlyOwner {owner = newOwner;}
    }// MyContract 继承了 Ownable
    contract MyContract is Ownable {uint256 public data;// 只有 owner 可以调用这个函数function setData(uint256 _data) public onlyOwner {data = _data;}// 重写了父合约的函数,并使用了 overridefunction transferOwnership(address newOwner) public override onlyOwner {// 在转移所有权前可以执行一些自定义逻辑require(newOwner != address(0), "Invalid address");// 调用父合约的实现super.transferOwnership(newOwner);}
    }
    
  2. 多重继承与线性化
    当合约继承多个父合约时,Solidity 使用 C3 线性化 来建立一个确定的继承顺序(一个“家族树”)。

    • 规则:继承顺序必须从“最基础”的合约到“最派生”的合约。
    • 使用 super:当子合约调用 super.someFunction() 时,它会按照线性化顺序调用下一个父合约的 someFunction
    contract A {function foo() public pure virtual returns (string memory) {return "A";}
    }contract B is A {function foo() public pure virtual override returns (string memory) {return "B";}
    }contract C is A {function foo() public pure virtual override returns (string memory) {return "C";}
    }// 多重继承:D 继承 B, C
    // 线性化顺序:D -> B -> C -> A
    contract D is B, C {function foo() public pure override(B, C) returns (string memory) {// 调用 super.foo() 会调用 C.foo()return string.concat("D -> ", super.foo());}
    }
    

    Solidity 的 super 是从当前合约的下一位置开始,沿着线性化顺序向后找,找到第一个定义了该函数的合约,并且在这个合约之后没有其他合约再次重写这个函数

在 D 中调用 super.foo()
  1. 从 D 的下一位置开始:B
  2. Bfoo() 函数 ✓
  3. 检查 B “之后”(C, A)是否有重写:
    • C 重写了 foo() ✗(B 之后有重写)
  4. 继续往后找:C
  5. Cfoo() 函数 ✓
  6. 检查 C “之后”(A)是否有重写:
    • Afoo(),但 A 是原始定义,不是重写 ✓(C 之后没有重写)
  7. 找到目标:C.foo()

所以 D 中的 super.foo() 指向 C.foo()

把"之后没有再重写"理解为:这个合约的实现在继承链中是"最终"的,后面没有子类再次修改它

就像这样:

D → B → C → A↑    ↑    ↑重写  重写  原始

当在 D 中找 super.foo() 时:

  • B 不是最终实现,因为后面的 C 又重写了
  • C 是最终实现,因为后面的 A 只是原始定义,不是重写

接口 vs. 继承:总结与关系

特性接口继承
目的定义外部交互的标准代码复用功能扩展
实现只有函数声明,无实现包含函数和状态变量的实现
关键字interfacecontract ... is ...
函数可见性必须是 external可以是 public, internal, private
状态变量不能有可以有
构造函数不能有可以有
修饰器不能有可以有
实例化不能
它们如何协同工作?

接口和继承经常一起使用。一个常见的模式是:

  1. 定义一个接口(如 IERC721),规定所有 NFT 合约应该有哪些功能。
  2. 创建一个基础实现合约(如 ERC721),它通过继承或其他方式,实现了接口中定义的所有函数。
  3. 你的具体合约(如 MyNFT)继承自这个基础实现合约 ERC721。这样,MyNFT 就自动符合了 IERC721 接口的标准。
// 1. 定义接口
interface IERC721 {function transferFrom(address from, address to, uint256 tokenId) external;
}// 2. 基础实现合约(通常来自 OpenZeppelin 这样的库)
contract ERC721 is IERC721 {// ... 实现复杂的状态变量和逻辑 ...function transferFrom(address from, address to, uint256 tokenId) external override {// ... 具体的实现代码 ...}
}// 3. 你的合约继承基础实现
contract MyNFT is ERC721 {// ... 你可以添加自己独有的功能 ...// 由于继承了 ERC721,它自动实现了 IERC721 接口
}

总结

  • 接口“做什么” 的契约,用于与外部合约进行标准化、无需信任的交互。
  • 继承“如何做” 的复用和扩展,用于在合约之间共享和构建逻辑。

熟练掌握接口和继承,是成为高级 Solidity 开发者的必经之路,它能让我们写出更清晰、更安全、更易于集成的智能合约。

http://www.dtcms.com/a/583391.html

相关文章:

  • 网站建设与维护高职wordpress站群主题
  • 网站改版 翻译制作网站用什么语言
  • 网页代理网站wordpress插件的页面文件
  • 咸阳做网站公司网站首页设计费用
  • 品牌网站如何建设大学生建设什么网站好
  • 哈尔滨+做网站公司有哪些wordpress自定义DIV样式
  • 电子商务网站建设汇报PPT网站右下角广告代码
  • 在凡科上做的网站无法加载出来网站开发的缓存技术
  • GUI自动化测试--自动化测试的意义和应用场景
  • ZCC7151S替代LTC7151S:高效静默,赋能未来电源设计——20V/15A同步降压稳压解决方案​
  • cocos打包的web-mobile(web手机端)在浏览器无法运行的原因和解决方法
  • 多种时间序列预测算法的MATLAB实现
  • 营销网站和展示型网站昆明做网站哪家公司好
  • 网站建设w亿玛酷1负责手机做炫光图头像的网站
  • 【JAVA虚函数与多态的底层实现】
  • 只做男士衬衫的网站建设网站公司怎么分工
  • 麦德龙网站建设目标网站页面布局用什么做
  • 3.1、Python-列表
  • 网站设计师培训做网站域名怎么选有利于seo
  • 餐饮网站建设方案书炫客网站建设
  • ABB RobotStudio许可功能premium不可用(从布局创建系统不成功)解决办法
  • 网站设置反爬虫的主要原因建筑网格布是用什么材料
  • 滕州网站建设 助企网络做相册网站logo
  • 潍坊网站建设 世纪环球16楼大航母网站建设在哪里
  • 九江网站网站建设兰州网站制作要多少钱
  • 东莞做网站公司首选高端建站方案
  • 一个人是否可以做公司网站把公司建设成为 现代化企业
  • 2025-11-07 ZYZ28-NOIP模拟赛-Round3 hetao1733837的record
  • 零知IDE——STM32F407VET6驱动SHT40温湿度传感器与ST7789实现智能环境监测系统
  • 中国人做的比较好的shopify网站公司网页怎么关闭