多くの著者がタイプとクラスという用語を同じ意味で使用していることがわかります。オブジェクトベースのモデルを扱う特定の教科書では、インターフェイスという用語も取り上げています。
オブジェクト指向プログラミング全般、特にC++/Java/オブジェクト指向データベースに基づいて、これらを簡単な言葉で説明してください。
多くの著者がタイプとクラスという用語を同じ意味で使用していることがわかります。オブジェクトベースのモデルを扱う特定の教科書では、インターフェイスという用語も取り上げています。
オブジェクト指向プログラミング全般、特にC++/Java/オブジェクト指向データベースに基づいて、これらを簡単な言葉で説明してください。
OOP 用語では、特に Java や C++ などの言語について話す場合は、タイプとクラスという用語を同じ意味で使用するのが非常に一般的です。
一般的に言えば、 Class を定義するときは、実際にはType templateを定義していることになります。ここから、定義しているTypeのオブジェクトが作成および/またはインスタンス化されます。
インターフェイスの概念はもう少し複雑です。インターフェイスを定義するときは、基本的にそれを使用するオブジェクトの Type を処理する特定の方法を指定します。つまり、オブジェクトは、そのインターフェイスを実装するクラスから取得する必要があります。インターフェイスは、元のクラス/タイプの仕様に含まれていない追加の動作を指定することにより、クラス/タイプを強化する方法です。
多くの実用的な目的のために、オブジェクトをそのインターフェイス全体でインターフェイス タイプであるかのように扱うことができます。これがおそらく、インターフェイスという用語がタイプという用語と同じ意味で使用される理由です。
詳細については、抽象データ型の背後にある理論を参照してください。
型とは、許容される値と許容される操作を示すデータの分類です。(ほとんど?) すべてのプログラミング言語には型がありますが、型付けの規律は言語ごとにかなり異なります。
クラスは、言語自体の特定の構文で定義される OOP 言語の特定の種類int
の型です (Javaなどのいわゆる「ネイティブ型」とは対照的に、float
適切な言語によって定義されます)。 . クラスは通常、データのメモリ レイアウトとエンコーディング (いわゆるメンバー変数) と、それらに対して機能する関数 (いわゆるメンバー関数またはメソッド) の観点から定義されます。
インターフェイス* は、指定された一連の同様の型の一部と見なされるために型が実装する必要がある操作の仕様ですが、許容される値、メモリ レイアウトなどを指定しません。
これは非常に非常に簡単な概要であり、これらに対するいくつかの言語のアプローチの単純化された「平均的な形式」のようなものです。いくつかのエッジ ケースや、インターフェイスとクラスの中間にあるものを作成する C++ の機能などは無視されます。また、Haskell のような関数型言語のクラスは無視されます。これは、脳にさらにダメージを与えることが目的ではないためです。;)
いくつかの例を追加するために編集
ここでは、概念を固めるのに役立つ Javaのような宣言をいくつか示します。
int myVariable1;
この変数myVariable1
— — は、2 の補数表記でエンコードされた 32 ビットの符号付き整数値で構成されるネイティブ (またはプリミティブ) 型です。既知の範囲 (約 -20 億から +20 億) と既知の一連の操作 (乗算、加算、除算、剰余、減算、さまざまな変換など) を使用できます。
class MyClass
{
int myMemberVariable;
int myOtherMemberVariable;
int myMethod(int p) { myMemberVariable += p; myOtherMemberVariable = p; }
}
MyClass myVariable2 = new MyClass();
これはclassmyVariable2
によって定義された型です。 メモリ レイアウト (この場合、2 の補数表記の 2 つの 32 ビット符号付き整数で構成されます) と、引数をその引数に追加して設定する単一の操作を定義します。 MyClass
MyClass
myMethod()
myMemberVariable
myOtherMemberVariable
interface MyInterface
{
int myInterfaceMethod(int p, int q);
}
ここでは、メンバー変数も実装もなしMyInterface
で、一連の操作 (この場合は単一の function で構成されます) のみを宣言します。myInterfaceMethod()
このインターフェイスを実装するすべてのクラスが、名前 + 戻り値 + 引数という特定のシグネチャを持つメソッドを持つことが保証されていることを示しているだけです。それを使用するには、インターフェースを実装するクラスを作成する必要があります。
class MyOtherClass implements MyInterface
{
int myMember1;
int myMember2;
int myMember3;
int myInterfaceMethod(int p, int q) { myMember1 = p; myMember2 = q; myMember3 = p - q; }
int myNonInterfaceMethod() { return myMember1; }
}
MyOtherClass myVariable3 = new MyOtherClass();
NowmyVariable3
は、3 つの符号付き 32 ビット整数と 2 つの演算で構成されるメモリ レイアウトを持つ型として定義されます。これらの操作の 1 つは、全体の部分のために実装する必要がある操作です。このようにして、操作がそこにあることが保証されているためimplements MyInterface
、(抽象) 型を期待するものはすべてMyInterface
(具体的な) 型を使用できます。MyOtherClass
もう 1 つのメソッド — — はmyNonInterfaceMethod()
MyInterfaceから来ていないため、 のみを期待しているものは、MyInterface
それが存在することを認識できないため、使用できません。
リクエストに応じていくつかの現実世界のものを追加するためにさらに編集
プログラムで整数値、浮動小数点値、文字列などを使用したことがある場合は、型を使用したことがあります。型は間違いなくコンピューティングの一部であり、私たちが行うことはすべて、特定の型の値の操作です。したがって、クラスとインターフェースの OOP 固有の概念に焦点を当てます。
データとそのデータに対する操作があるときはいつでも、クラスの可能性があります。銀行口座を例にとってみましょう。銀行口座には、とりわけ、口座番号、現在の残高、取引限度額などがあります。これを表すクラス (下手で、概念を説明するためだけに示されています) は、次のようになります。
class BankAccount
{
String accountNumber;
float balance; /* DO NOT USE FLOATING POINT IN REAL FINANCIAL CODE! */
int transaction_limit;
float transaction(float change) {
balance += change > transaction_limit ? transaction_limit : change;
return balance;
}
}
String
これで、この型の変数を作成して、それが口座番号 (それ自体が型) や残高 (それ自体が型)を保持することがわかりますが、float
実際の金融コードでは浮動小数点を使用しないでください!)およびトランザクション制限(それ自体がint
タイプです)。transaction
また、トランザクション制限に対して変更をチェックし、残高を変更するトランザクション (創造的には と呼ばれる) を実行できることも知っています。(このための実際のクラスには、さらに多くのものが含まれており、教育目的で削除した多くの難読化された保護が含まれています。)
ここで、銀行口座だけでなく、いくつかの種類の取引が存在する、より洗練された金融環境にいるとしましょう。さらに、基になる型の詳細を気にしないトランザクションを処理するコードがあるとします。たとえば、銀行口座、売掛金勘定などを対象とする、トランザクションのオフライン バッチ プロセッサです。書籍内のすべての種類のトランザクションについて知らせるのではなく、代わりにこれを行うことができます。
interface Transactable
{
float transaction(float change);
}
class BankAccount implements Transactable
{
/* interior is identical */
}
class ReceivablesAccount implements Transactable
{
float balance;
float transaction(float change) { balance += change; }
}
型について知っているものはすべて、のインスタンスと のインスタンスのTransactable
両方を使用できるようになりました。銀行口座には取引制限があり、売掛金口座には制限がないことを知る必要はありません。データの内部表現について何も知る必要はありません。彼らは何かの特殊なケースを知る必要はありません。1 つの関数について名前 ( ) で知っていればよいだけです。(これをより具体的に実際に使用したい場合は、「for in」ループは言うまでもなく、コレクションクラスがJavaでインターフェースをどのように使用しているかを見てください。)BankAccount
ReceivablesAccount
transaction()
Iterable
個人的には、具体的に書いた方がわかりやすいと思います。たとえば、キッチンにあるものの在庫を追跡するプログラムを作成したいとしましょう。
ほとんどの言語は、整数、文字、バイトなどの非常に単純でプリミティブな「型」しか認識していません。これらの基本的なタイプを完全に使用して、キッチンにあるもののタイプを記述することができます。たとえば、配列内の各インデックスが何かの属性を表す文字配列の配列を作成できます。同様に、おそらく、インデックス 0 は、記述しようとしているものの「タイプ」を表します。しかし、もちろん、この手法はすぐに非常に複雑になります。
したがって、オブジェクト指向の手法を使用すると、プリミティブ型を組み合わせて新しい型を簡単に定義できます。その方法は、クラス定義を作成することです。クラスを新しいタイプの設計図と考えてください。
キッチンの例では、「食品」タイプ、「家電」タイプ、「家具」タイプの概念があります。これらの新しい型をプログラムで使用するには、それぞれのクラス定義を作成する必要があります。
public class Food {...}
public class Appliance {...}
public class Furniture {...}
インターフェイスは、何かとやり取りする方法を説明します。たとえば、キッチンにあるものの在庫を作成したいとします。在庫に「追加」したり、在庫から物を「削除」したり、在庫を「反復」したりできるようにしたいと考えています。アクション「追加」、「削除」、および「反復」は、ほとんどすべてのリストに適用されます (在庫だけでなく)。Java は、この目的のために「List」という名前のインターフェースを提供します。非常にポピュラーで一般的な List "Interface" を使用してインベントリ オブジェクトを作成することにより、すべてのアクションを無料で取得でき、他のプログラマーはプログラムを見て、"Inventory" オブジェクトとやり取りする方法を正確に知ることができます。
...
java.util.List inventory = new ArrayList();
...
//later in our program, we can interact with inventory
// with any of methods on the list interface (because ArrayList "implements" List)
inventory.add(new Furniture("chair"));
inventory.add(new Appliance("refrigerator"));
inventory.add(new Food("ice cream"));
...
したがって、上記の例では、在庫は ArrayList 型のインスタンスを含む変数です。ArrayList クラス定義は「List」インターフェースを実装するため、Type ArrayList のすべてのインスタンスは、「List」インターフェースで定義された規約に従う必要があります。inventory
つまり、次のように (ArrayList ではなく) LinkedList と同じくらい簡単に作成できます。
java.util.List inventory = new LinkedList();
インターフェイスは、実行する特定の一連のアクションを公開するコントラクトと考えてください。LinkedList と ArrayList はどちらも「List」インターフェースを実装しているため、「List」インターフェースのすべてのメソッドを実装することが契約上義務付けられています。
これの素晴らしい点は、「在庫」変数の使用に関心のあるコードは、「追加」、「削除」、または「反復子」アクションの実装方法についてがらくたを与えないことです。彼らが気にするのは、変数「在庫」が「リスト」インターフェースに従うことだけです。
つまり、LinkedList は ArrayList とは少し異なる「add」メソッドを実装しています。しかし、それは、「在庫」変数を使用したいコードにとっては問題ではありません。
数学の達人がリストを保存する革新的な方法を思いついた場合。おそらく彼らは、コンピュータのメモリ内ではなく月にリストを保存する方法を見つけ出し、ArrayList よりも 10 倍高速であることが判明しました。あとは、次のようにするだけです。
java.util.List inventory = new CrazyMoonList();
CrazyMoonList は「追加」、「削除」などを提供することが保証されているため、インベントリを使用する他のすべてのコードはまったく同じままでかまいません。
これが概念を明確にするのに役立つことを願っています!
クラスはオブジェクトのテンプレートであり、変数、プロパティ、およびメソッドを含むユーザー定義のデータ型です。
//defines a simple class describing all types of fruit
public class Fruit {
//insert fields here...
Fruit(){//constructor method
//insert constructor code here...
}
//insert class methods here....
}
このクラスをインスタンス化してオブジェクトにするときは、コンパイラにオブジェクトの型を知らせる必要があります。この場合、オブジェクトは Fruit 型になります。
//The initialisation might look something like this.
Fruit myFruit = new Fruit();
前のコード行は、Fruit 型の新しいオブジェクトを構築するようコンパイラに指示します。
つまり、簡単に言えば、「クラス」はオブジェクトの特性 (そのデータ) を定義します。「タイプ」は、インスタンス化されたときに対象となるオブジェクトのタイプ (果物、車、机など) を指します。
この概念は、継承とポリモーフィズムのトピックをさらに読むことで拡張できます。
これがお役に立てば幸いです。