継承とインターフェース
共通部分を親にまとめ、子が引き継ぐ
自動販売機にはいろいろな種類があります——ジュース用、お菓子用、切符用。でも「お金を受け取る」「商品を出す」という基本は共通です。共通部分を親の設計図にまとめ、子がそれを引き継いで拡張する。これが今回の継承です。
継承 = 設計図の引き継ぎ
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Machine {
function greet() public pure virtual returns (string memory) {
return "I am a machine";
}
}
contract AutoVending is Machine {
function greet() public pure override returns (string memory) {
return "I am a vending machine!";
}
}
新しい言葉は 3 つだけです。
- is —
contract AutoVending is Machine= 「AutoVending は Machine の設計図を引き継ぐ」。Machine を親、AutoVending を子と呼びます。子は、親に書いてある変数や関数を自分で書かなくても持っています - virtual — 親側の印。「この関数は、子が書き換えてもよい」という許可です
- override — 子側の印。「親の関数を、わたし流に書き換えます」という宣言です
greet に virtual(親)と override(子)が付いているので、AutoVending の greet() を呼ぶと、子の答え "I am a vending machine!" が返ります。親が許可し、子が宣言する——両方そろって初めて書き換えられる、安全設計です。
なお returns (string memory) の memory は前回やった「データの置き場所」です。string を返すときは付ける、でしたね。
インターフェース = 中身のないボタン一覧
interface(インターフェース)は、「このボタンがあります」という一覧表だけを定めたものです。中身(手順)は書けません。
interface IVending {
function buy() external;
function getPrice() external view returns (uint256);
}
- 名前は慣習で
Iから始めます(IVending = Vending の一覧表) - 関数は
external(外から使う前提)で、中身なしの;止め - コントラクトが
contract MyMachine is IVending { ... }と名乗ったら、一覧表にあるボタンを全部実装する義務が生じます
「なんの役に立つの?」——他人のコントラクトと会話するときの共通の窓口になります。相手の中身を知らなくても、一覧表さえ合っていれば呼び出せる。実際の開発では、トークン(ERC-20 など)の世界標準がこの interface で定義されています。今は「そういう約束ごとの道具がある」とわかれば十分です。
:::note interface は読むだけで OK この例を Lab のファイルに足してコンパイルすると、エラーになることがあります(interface には実行の中身がないため、Lab がデプロイ対象に interface を選んでしまうと失敗します)。今回は写さず、目で読んで「一覧表なんだな」とわかれば十分です。 :::
今回覚えたこと
- is = 設計図の引き継ぎ(継承)。子は親の変数・関数をそのまま持つ
- virtual(親: 書き換え許可)と override(子: 書き換え宣言)はセット
- interface = 中身のないボタン一覧表。名乗ったら全部実装する義務
次回は、第4回で覚えた require の仲間たち——エラー処理をまとめて学びます。
:::tip Lab でやってみる
上の継承コードをそのままコンパイル→デプロイしてみましょう。1 つのファイルに contract を複数書いたとき、Lab では名前のアルファベット順で最初のものがデプロイされます(この例では AutoVending が Machine より先)。実行画面で greet() を呼ぶと、子の答え "I am a vending machine!" が返ってくるはずです。override の中の文字列を変えて再デプロイすれば、答えが変わることも確かめられます。操作方法: コントラクトを動かす
:::