Java は、私がキャリアの中で最初に学んだ言語です。その構造は、プログラミングの概念を理解し始めた初期の頃の基礎となりました。まったく異なるアプローチを持つ他のいくつかの言語を経験したことで、私の視点は広がりました。今日は、継承の考え方について考えてみたいと思います。
Java では、継承の考え方はサブタイプ化の概念と密接に結びついています。サブタイプ化はIS A関係の実装です。たとえば、 Rabbit
クラスはMammal
クラスのサブタイプです。したがって、 Rabbit
インスタンスはMammal
のすべての動作を持ちます。つまり、動作を継承します。
このため、メソッドがMammal
パラメータを呼び出すときにRabbit
インスタンスを渡したり、メソッドの戻り値の型がMammal
ときにRabbit
インスタンスを返したりできます。Java、.Net、またはそれに似たものを学習したことがあるなら、継承をこのように捉えており、それが新しい標準になります。
明示的な継承です。
class Animal { void feed(); } class Rabbit extends Animal { //1 }
Rabbit
Animal
IS A
ので、 feed()
初めて Go を見たとき、継承を提供しながらサブタイプがないことに驚きました。Go はダックタイピングを使用します。
もしそれがアヒルのように見え、アヒルのように泳ぎ、アヒルのように鳴くなら、それはおそらくアヒルでしょう。
Go struct
インターフェースと同じ関数を実装する場合、暗黙的にインターフェースを実装します。
type Animal interface { feed() //1 } type Rabbit struct { } func (rabbit Rabbit) feed() { //2 // feeds }
Animal
餌を与えることができるRabbit
パラメータとして受け取るfeed()
関数が存在するため、 Rabbit
Animal
実装します。
私は Go のエラー処理アプローチが嫌いですが、暗黙的な実装については二つの考えがありました。一方では、それが新しい概念であることを理解し、偏見を持たないように努めました。他方では、ソフトウェア開発でも実生活でも、物事は常に暗黙的よりも明示的である方が良いと考えています。
Python は、継承に関して私が知る限り最も興味深い言語です。
サブタイプと型ベースの継承は、Python の誕生当初から存在しています。
class Animal: def feed(self): #1 pass #2 class Rabbit(Animal): #3 pass
Animal
餌を与えることができるRabbit
Animal
IS A
ので、 feed()
この点では、継承に関しては Python は Java と同じように動作します。Python はダックタイピングも提供しており、これをマジックメソッドと呼んでいます。たとえば、反復可能(イテレータを返すことができる) なものを作成するには、 __iter__()
と__next__()
を実装するだけで済みます。
class SingleValueIterable(): done = False def __init__(self, value): self.value = value def __iter__(self): #1 return self def __next__(self): #1 if self.done: raise StopIteration else: self.done = True return self.value svi = SingleValueIterable(5) sviter = iter(svi) #2 for x in sviter: print(x) #3
Iterator
を作成する - Pythonは上記のメソッドを実装しているので、その方法を知っています5
このダックタイピングのアプローチの問題は、Python の定義済みマジックメソッドに対してのみ機能することです。サードパーティが暗黙的に継承できるクラスを提供したい場合はどうすればよいでしょうか。
class Animal: def feed(): pass class Rabbit: def feed(): pass
上記のスニペットでは、残念なことにRabbit
Animal
はありません。そこで、Protocols: Structural subtyping (static duck Typing) というタイトルのPEP 544が登場します。この PEP は、クラスにマジック メソッドを定義することの不可能性を解決します。この PEP は、単純なProtocol
クラスを定義します。このクラスを継承すると、クラスで定義されたメソッドはダック タイピングの対象となり、これが static duck Typing という名前につながっています。
from typing import Protocol class Animal(Protocol): #1 def feed(): #2 pass class Rabbit: def feed(): #2 pass class VenusFlytrap: def feed(): #2 pass
Protocol
から継承Animal
Protocol
なので、 feed()
を定義するクラスは良くも悪くもAnimal
になります。オブジェクト指向プログラミング、継承、サブタイプ化は、最初に学ぶ言語によっては、他の言語には当てはまらない特定の意味を持つ場合があります。Java はオブジェクト指向言語であると自称しており、完全なパッケージを提供しています。Go は OO 言語ではありませんが、ダックタイピングによるサブタイプ化を提供しています。Python は明示的継承と暗黙的継承の両方を提供していますが、インターフェースは提供していません。
新しいプログラミング言語を学ぶには、すでに知っている言語と比較します。言語の機能を知ることは、ターゲット言語で慣用的なコードを書くための鍵となります。知っている言語にはない機能に慣れておくと、プログラミング全体に対する理解が広がります。
さらに詳しく:
2025年1月26日にA Java Geekで最初に公開されました