乐闻世界logo
搜索文章和话题

How does inheritance work in Solidity? What are the differences between abstract contracts and interfaces?

3月6日 21:50

Solidity supports object-oriented programming inheritance features, allowing contracts to reuse code and establish hierarchical relationships. Meanwhile, abstract contracts and interfaces provide mechanisms for defining standard specifications.

1. Basic Inheritance

Solidity uses the is keyword for inheritance and supports multiple inheritance.

solidity
// Parent contract contract Animal { string public name; constructor(string memory _name) { name = _name; } function speak() public pure virtual returns (string memory) { return "Some sound"; } } // Child contract inherits parent contract contract Dog is Animal { constructor(string memory _name) Animal(_name) {} // Override parent contract function function speak() public pure override returns (string memory) { return "Woof!"; } } // Multiple inheritance contract PetDog is Animal, Pet { constructor(string memory _name) Animal(_name) Pet(_name) {} function speak() public pure override(Animal, Pet) returns (string memory) { return "Friendly Woof!"; } }

2. Constructor Inheritance

Child contracts need to explicitly call parent contract constructors.

solidity
contract Parent { uint256 public value; constructor(uint256 _value) { value = _value; } } // Method 1: Pass parameters in inheritance list contract Child1 is Parent(100) { // value automatically set to 100 } // Method 2: Pass parameters in child contract constructor contract Child2 is Parent { constructor(uint256 _value) Parent(_value) { // Can dynamically set value } } // Method 3: Use input parameters contract Child3 is Parent { constructor(uint256 _value) Parent(_value * 2) { // Can process parameters } }

3. Function Override

Use virtual and override keywords to control function overriding.

solidity
contract Base { // virtual allows child contracts to override function getValue() public pure virtual returns (uint256) { return 1; } // No virtual, cannot override function fixedValue() public pure returns (uint256) { return 100; } } contract Derived is Base { // override indicates overriding parent contract function function getValue() public pure override returns (uint256) { return 2; } // Error: cannot override function without virtual // function fixedValue() public pure override returns (uint256) { // return 200; // } }

4. Multiple Inheritance and Linearization

Solidity uses C3 linearization algorithm to resolve multiple inheritance conflicts.

solidity
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"; } } // Multiple inheritance: D inherits B and C // Order matters! D is B, C means B comes before C contract D is B, C { // Must override foo because both B and C override A's foo function foo() public pure override(B, C) returns (string memory) { return super.foo(); // Calls C.foo() (rightmost base class) } } // Different order, different result contract E is C, B { function foo() public pure override(C, B) returns (string memory) { return super.foo(); // Calls B.foo() } }

5. Abstract Contract

Abstract contracts contain at least one unimplemented function and cannot be deployed directly.

solidity
// Abstract contract abstract contract Animal { string public name; constructor(string memory _name) { name = _name; } // Unimplemented function (abstract function) function speak() public pure virtual returns (string memory); // Implemented function function getName() public view returns (string memory) { return name; } } // Concrete contract must implement all abstract functions contract Dog is Animal { constructor(string memory _name) Animal(_name) {} function speak() public pure override returns (string memory) { return "Woof!"; } } // Error: cannot deploy abstract contract directly // Animal animal = new Animal("Test"); // compilation error!

6. Interface

Interfaces are stricter abstractions that can only contain function declarations, not implementations or state variables.

solidity
// ERC20 interface example interface IERC20 { // Function declarations (no implementation) function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); // Events event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // Implement interface contract MyToken is IERC20 { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; function totalSupply() external view override returns (uint256) { return _totalSupply; } function balanceOf(address account) external view override returns (uint256) { return _balances[account]; } function transfer(address to, uint256 amount) external override returns (bool) { // Implementation logic... return true; } function allowance(address owner, address spender) external view override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) external override returns (bool) { // Implementation logic... return true; } function transferFrom(address from, address to, uint256 amount) external override returns (bool) { // Implementation logic... return true; } }

7. Abstract Contract vs Interface Comparison

FeatureAbstract ContractInterface
Constructor✅ Can have❌ Cannot have
State variables✅ Can have❌ Cannot have
Function implementation✅ Can have❌ Cannot have
Function visibilityAnyMust be external
Inherit other contracts✅ Can❌ Cannot (only inherit interfaces)
Modifiers✅ Can have❌ Cannot have
Use casesPartially implemented base contractsCompletely abstract specification definitions

8. Practical Application Examples

Using Abstract Contracts to Build Base Functionality

solidity
abstract contract Ownable { address public owner; constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } } abstract contract Pausable is Ownable { bool public paused; modifier whenNotPaused() { require(!paused, "Paused"); _; } function pause() public onlyOwner { paused = true; } function unpause() public onlyOwner { paused = false; } } contract MyContract is Pausable { function sensitiveOperation() public whenNotPaused { // Only executable when not paused } }

Using Interfaces for Standard Compatibility

solidity
interface IERC721 { function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address owner); function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); } contract NFTMarketplace { // Use interface to interact with any ERC721 contract function buyNFT(address nftContract, uint256 tokenId) public payable { IERC721 nft = IERC721(nftContract); address seller = nft.ownerOf(tokenId); // Verify payment... nft.safeTransferFrom(seller, msg.sender, tokenId); } }

9. Inheritance Best Practices

  1. Use OpenZeppelin Standard Libraries:
solidity
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyToken is ERC20, Ownable { constructor() ERC20("MyToken", "MTK") { _mint(msg.sender, 1000000 * 10 ** decimals()); } }
  1. Pay attention to inheritance order:
solidity
// Correct: base contracts first contract Good is Ownable, Pausable, ERC20 {} // Avoid: messy order may cause unexpected behavior contract Bad is ERC20, Pausable, Ownable {}
  1. Explicitly use override:
solidity
function transfer(address to, uint256 amount) public override(ERC20, IERC20) // Explicitly specify overridden interface returns (bool) { return super.transfer(to, amount); }
  1. Reasonable use of abstraction and interfaces:
  2. Use abstract contracts when partial implementation is needed
  3. Use interfaces when defining standard specifications
  4. Avoid over-engineering, keep it simple
标签:Solidity