{ }codelper

学ぶ › TypeScript

言語ごとの特徴 · 37

クラスとオブジェクト

データ(フィールド)と動作(メソッド)をひとつの型にまとめ、その型からオブジェクトをいくつも作ります。

この単元は Python · JavaScript · TypeScript · Java · C# · C++ 向けです。
C にはクラスがなく、構造体(struct)と関数で同じことをします(構造体の単元を参照)。
Craykon には自分で作るクラスがなく、辞書() に 学生["名前"] のように入れます。

クラスは設計図で、オブジェクトはその設計図から作った実物です。
下の例は名前と点数を持つ Student クラスで学生を何人か作り、点数の高い順に並べて成績といっしょに表示します。

import { readFileSync } from "node:fs"; class Student {  readonly name: string;  score: number;   constructor(name: string, score: number) {    this.name = name;    this.score = score;  }   grade(): string {    if (this.score >= 90) return "A";    if (this.score >= 80) return "B";    return "C";  }   describe(): string {    return `${this.name} ${this.score} ${this.grade()}`;  }} const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const n: number = Number(lines[0]);const students: Student[] = [];for (let i = 1; i <= n; i++) {  const [name, score] = lines[i].trim().split(" ");  students.push(new Student(name, Number(score)));} students.sort((a, b) => b.score - a.score);for (const s of students) console.log(s.describe());
stdin
3
Mina 82
Joon 95
Sora 78
stdout
Joon 95 A
Mina 82 B
Sora 78 C

ポイント

  • クラスには、オブジェクトが持つデータ(フィールド)とオブジェクトができること(メソッド)を書きます。
  • コンストラクタは、オブジェクトを作るときに最初の値を入れる特別な関数です。
  • メソッドの中でオブジェクト自身は this(Python は self)です。
    引数と名前が重なるときは this.name = name のように区別します。
  • アクセス修飾子(public・private)で、外から使えるメンバーを決めます。
    フィールドはふつう隠して、メソッドやプロパティで読ませます。
  • オブジェクトのリストはフィールドを基準に並べ替えます。
    並べ替えの関数に「何を比べるか」を渡します。

言語ごとの書き方

Pythonclass Student: の中にコンストラクタ def __init__(self, name, score):。
フィールドは self.name = name のように作り、すべてのメソッドの最初の引数は self。
オブジェクトは new なしで Student("Mina", 82)。
並べ替え: students.sort(key=lambda s: s.score, reverse=True)
JavaScriptclass Student { constructor(name, score) { this.name = name; } }。
オブジェクトは new Student("Mina", 82) で作り、new を忘れると TypeError: Class constructor Student cannot be invoked without 'new'。
並べ替え: students.sort((a, b) => b.score - a.score)
TypeScriptJavaScript と同じ形で、フィールドを score: number; のように先に宣言します。
private・readonly はコンパイル時だけの検査で、実行時には消えます。
本当に隠すなら JavaScript の非公開フィールド #score
Javaコンストラクタはクラス名と同じで、戻り値の型を書きません。
フィールドは private で隠し、getScore() のようなゲッターで読みます。
修飾子を書かないと同じパッケージの中だけで見えます。
並べ替え: students.sort(Comparator.comparingInt(Student::getScore).reversed())
C#プロパティ public int Score { get; } を使うと、外からは s.Score とフィールドのように読めます。
修飾子を書かないメンバーは private。
並べ替え: students.Sort((a, b) => b.Score.CompareTo(a.Score))
C++class は既定が private、struct は public で、違いはそれだけなので public:・private: を自分で書きます。
コンストラクタは初期化リスト : name_(name), score_(score)、メソッドの後ろの const は「オブジェクトを変えない」という約束。
オブジェクトは new なしで値として Student s("Mina", 82);。
並べ替え: std::sort にラムダ [](const Student& a, const Student& b) { return a.score() > b.score(); }

よくある間違い

メソッドに self を書き忘れたPython

class Student:
    def grade():
        return "A"

Student().grade()

→ TypeError: Student.grade() takes 0 positional arguments but 1 was given。メソッドの中で self. なしに score と書くと NameError です

最初の引数に self を置き、フィールドはいつも self.score のように読みます。

既定値にリスト [] を置いたPython

def __init__(self, name, tags=[]):
    self.tags = tags

→ そのリストは def が実行されたときに一度だけ作られます。ある学生に入れたタグがほかの学生にも見えます

tags=None で受け取り、コンストラクタの中で新しいリストを作ります。

オブジェクトの中身を == で比べたJava · C#

new Student("Mina", 82) == new Student("Mina", 82)

→ false — == は同じオブジェクトかどうかを見ます。equals も定義し直すまでは == とまったく同じ動きです

中身は equals(C# は Equals)で比べ、そのメソッドを自分で定義し直すか record を使います。

言語ごとの違い
b = a のあと b を変えると、Python・JavaScript・TypeScript・Java・C# では a も変わります(2つの名前が同じオブジェクトを指すからです)。
C++ はオブジェクトが値なので Student b = a; や for (Student s : students) はコピーを作り、元のものを扱うには Student&(読むだけなら const Student&)を使います。

練習問題