Mungkin pertama-tama berguna untuk membedakan antara suatu tipe dan kelas dan kemudian menyelami perbedaan antara subtipe dan subklas.
Untuk sisa jawaban ini saya akan berasumsi bahwa tipe dalam diskusi adalah tipe statis (karena subtyping biasanya muncul dalam konteks statis).
Saya akan mengembangkan pseudocode mainan untuk membantu mengilustrasikan perbedaan antara tipe dan kelas karena sebagian besar bahasa mengacaukan mereka setidaknya sebagian (untuk alasan yang baik saya akan menyinggung secara singkat).
Mari kita mulai dengan suatu tipe. Tipe adalah label untuk ekspresi dalam kode Anda. Nilai label ini dan apakah konsisten (untuk beberapa jenis definisi spesifik sistem-spesifik) dengan semua nilai label lain dapat ditentukan oleh program eksternal (pengetik ketik) tanpa menjalankan program Anda. Itulah yang membuat label-label ini istimewa dan pantas untuk namanya sendiri.
Dalam bahasa mainan kami, kami mungkin mengizinkan pembuatan label seperti itu.
declare type Int
declare type String
Maka kita mungkin memberi label berbagai nilai sebagai tipe ini.
0 is of type Int
1 is of type Int
-1 is of type Int
...
"" is of type String
"a" is of type String
"b" is of type String
...
Dengan pernyataan ini, juru ketik kami sekarang dapat menolak pernyataan seperti
0 is of type String
jika salah satu persyaratan dari sistem tipe kami adalah bahwa setiap ekspresi memiliki tipe yang unik.
Mari kita kesampingkan untuk sekarang betapa kikuknya ini dan bagaimana Anda akan mengalami masalah dalam menetapkan jumlah jenis ekspresi yang tak terbatas. Kita bisa kembali lagi nanti.
Kelas di sisi lain adalah kumpulan metode dan bidang yang dikelompokkan bersama (berpotensi dengan pengubah akses seperti pribadi atau publik).
class StringClass:
defMethod concatenate(otherString): ...
defField size: ...
Sebuah instance dari kelas ini mendapatkan kemampuan untuk membuat atau menggunakan definisi yang sudah ada sebelumnya dari metode dan bidang ini.
Kita bisa memilih untuk mengaitkan kelas dengan tipe sehingga setiap instance kelas secara otomatis dilabeli dengan tipe itu.
associate StringClass with String
Tetapi tidak setiap jenis perlu memiliki kelas terkait.
# Hmm... Doesn't look like there's a class for Int
Bisa dibayangkan juga bahwa dalam bahasa mainan kita tidak setiap kelas memiliki tipe, terutama jika tidak semua ekspresi kita memiliki tipe. Agak sulit (tapi bukan tidak mungkin) untuk membayangkan seperti apa aturan konsistensi sistem tipe jika beberapa ekspresi memiliki tipe dan beberapa tidak.
Terlebih lagi dalam bahasa mainan kita, asosiasi ini tidak harus unik. Kita bisa mengasosiasikan dua kelas dengan tipe yang sama.
associate MyCustomStringClass with String
Sekarang ingatlah bahwa tidak ada keharusan bagi juru ketik kami untuk melacak nilai ekspresi (dan dalam kebanyakan kasus tidak akan atau tidak mungkin melakukannya). Yang ia tahu hanyalah label yang Anda beri tahu. Sebagai pengingat sebelumnya, typechecker hanya dapat menolak pernyataan itu 0 is of type String
karena aturan jenis yang dibuat secara artifisial bahwa ekspresi harus memiliki tipe unik dan kami sudah memberi label ekspresi 0
lain. Itu tidak memiliki pengetahuan khusus tentang nilai 0
.
Jadi bagaimana dengan subtyping? Subtipe dengan baik adalah nama untuk aturan umum dalam pengetikan yang merelaksasi aturan lain yang mungkin Anda miliki. Yaitu jika A is subtype of B
kemudian di mana-mana typechecker Anda meminta label B
, itu juga akan menerima A
.
Sebagai contoh, kita dapat melakukan hal berikut untuk nomor kita daripada apa yang kita miliki sebelumnya.
declare type NaturalNum
declare type Int
NaturalNum is subtype of Int
0 is of type NaturalNum
1 is of type NaturalNum
-1 is of type Int
...
Subclassing adalah singkatan untuk mendeklarasikan kelas baru yang memungkinkan Anda untuk menggunakan kembali metode dan bidang yang sebelumnya dinyatakan.
class ExtendedStringClass is subclass of StringClass:
# We get concatenate and size for free!
def addQuestionMark: ...
Kami tidak harus mengaitkan contoh ExtendedStringClass
dengan String
seperti yang kami lakukan StringClass
sejak, setelah semua itu adalah kelas yang sama sekali baru, kami hanya tidak perlu menulis terlalu banyak. Ini akan memungkinkan kami untuk memberikan ExtendedStringClass
jenis yang tidak kompatibel dengan String
dari sudut pandang pengetik huruf.
Demikian juga kita dapat memutuskan untuk membuat kelas yang baru NewClass
dan selesai
associate NewClass with String
Sekarang setiap instance StringClass
dapat diganti dengan NewClass
dari sudut pandang typechecker.
Jadi dalam teori, subtyping dan subclassing adalah hal yang sangat berbeda. Tapi tidak ada bahasa yang saya tahu yang memiliki tipe dan kelas yang benar-benar melakukan hal-hal seperti ini. Mari kita mulai mengurangi bahasa kita dan menjelaskan alasan di balik beberapa keputusan kita.
Pertama, meskipun dalam teori kelas yang benar-benar berbeda dapat diberikan jenis yang sama atau kelas dapat diberi jenis yang sama dengan nilai-nilai yang bukan contoh dari kelas apa pun, ini sangat menghambat kegunaan dari pengetik huruf. Typechecker dirampas secara efektif dari kemampuan untuk memeriksa apakah metode atau bidang yang Anda panggil dalam ekspresi benar-benar ada pada nilai itu, yang mungkin merupakan cek yang Anda inginkan jika Anda akan kesulitan bermain bersama dengan typechecker. Lagi pula, siapa yang tahu apa sebenarnya nilai di bawah String
label itu; itu mungkin sesuatu yang tidak memiliki, misalnya, concatenate
metode sama sekali!
Oke jadi mari kita menetapkan bahwa setiap kelas secara otomatis menghasilkan tipe baru dengan nama yang sama dengan kelas itu dan associate
contoh dengan tipe itu. Itu memungkinkan kita menyingkirkan associate
serta berbagai nama antara StringClass
dan String
.
Untuk alasan yang sama, kami mungkin ingin secara otomatis membangun hubungan subtipe antara tipe dua kelas di mana satu adalah subkelas dari yang lain. Setelah semua subclass dijamin memiliki semua metode dan bidang yang dilakukan oleh kelas induknya, tetapi kebalikannya tidak benar. Oleh karena itu sementara subclass dapat lulus kapan saja Anda membutuhkan jenis kelas induk, jenis kelas induk harus ditolak jika Anda memerlukan jenis subclass.
Jika Anda menggabungkan ini dengan ketentuan bahwa semua nilai yang ditentukan pengguna harus merupakan instance dari sebuah kelas, maka Anda dapat is subclass of
menarik dua tugas dan menyingkirkannya is subtype of
.
Dan ini membawa kita pada karakteristik yang dimiliki oleh sebagian besar bahasa OO yang diketik secara statis. Ada satu set "primitif" jenis (misalnya int
, float
, dll) yang tidak terkait dengan setiap kelas dan tidak ditetapkan pengguna. Kemudian Anda memiliki semua kelas yang ditentukan pengguna yang secara otomatis memiliki jenis nama yang sama dan mengidentifikasi subkelas dengan subtyping.
Catatan terakhir yang akan saya buat adalah tentang kekakuan dari mendeklarasikan tipe secara terpisah dari nilai. Sebagian besar bahasa mengacaukan pembuatan keduanya, sehingga deklarasi tipe juga merupakan deklarasi untuk menghasilkan nilai yang sama sekali baru yang secara otomatis dilabeli dengan tipe itu. Sebagai contoh, deklarasi kelas biasanya menciptakan tipe dan juga cara instantiating nilai dari tipe itu. Ini menghilangkan beberapa kecerobohan dan, di hadapan konstruktor, juga memungkinkan Anda membuat label nilai tak terhingga banyak dengan tipe dalam satu pukulan.