บางทีมันอาจมีประโยชน์ในการแยกความแตกต่างระหว่างชนิดและคลาสจากนั้นดำดิ่งลงไปในความแตกต่างระหว่างการพิมพ์ย่อยและการแบ่งคลาสย่อย
สำหรับส่วนที่เหลือของคำตอบนี้ฉันจะสมมติว่าประเภทในการสนทนาเป็นประเภทคงที่ (เนื่องจากพิมพ์ย่อยมักจะเกิดขึ้นในบริบทคงที่)
ฉันจะพัฒนารหัสจำลองของเล่นเพื่อช่วยอธิบายความแตกต่างระหว่างประเภทและคลาสเนื่องจากภาษาส่วนใหญ่พูดคุยกันอย่างน้อยส่วนหนึ่ง (ด้วยเหตุผลที่ดีที่ฉันจะสัมผัสสั้น ๆ )
เริ่มจากประเภทกันก่อน ประเภทคือป้ายกำกับสำหรับการแสดงออกในรหัสของคุณ ค่าของเลเบลนี้และไม่ว่าจะสอดคล้องกันหรือไม่ (สำหรับคำจำกัดความเฉพาะระบบบางประเภทที่สอดคล้องกัน) กับค่าของเลเบลอื่นทั้งหมดสามารถถูกกำหนดโดยโปรแกรมภายนอก (ตัวตรวจสอบชนิด) โดยไม่เรียกใช้โปรแกรมของคุณ นั่นคือสิ่งที่ทำให้ป้ายกำกับเหล่านี้พิเศษและควรได้รับชื่อของตัวเอง
ในภาษาของเล่นของเราเราอาจอนุญาตสำหรับการสร้างป้ายกำกับเช่นนั้น
declare type Int
declare type String
จากนั้นเราอาจติดป้ายกำกับค่าต่างๆว่าเป็นประเภทนี้
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
...
ด้วยข้อความเหล่านี้ typechecker ของเราสามารถปฏิเสธข้อความเช่น
0 is of type String
หากหนึ่งในข้อกำหนดของระบบพิมพ์ของเราคือว่าทุกการแสดงออกมีประเภทที่ไม่ซ้ำกัน
ตอนนี้เรามาดูกันว่า clunky เป็นอย่างไรและคุณจะมีปัญหาในการกำหนดประเภทของนิพจน์ได้อย่างไร เราสามารถกลับไปหามันในภายหลัง
คลาสในอีกทางหนึ่งคือชุดของวิธีการและเขตข้อมูลที่จัดกลุ่มเข้าด้วยกัน (อาจมีการปรับเปลี่ยนการเข้าถึงเช่นส่วนตัวหรือสาธารณะ)
class StringClass:
defMethod concatenate(otherString): ...
defField size: ...
อินสแตนซ์ของคลาสนี้ได้รับความสามารถในการสร้างหรือใช้คำจำกัดความที่มีอยู่ก่อนของวิธีการและฟิลด์เหล่านี้
เราสามารถเลือกที่จะเชื่อมโยงคลาสกับประเภทเช่นว่าทุกอินสแตนซ์ของคลาสจะมีป้ายกำกับด้วยประเภทนั้นโดยอัตโนมัติ
associate StringClass with String
แต่ไม่ใช่ทุกประเภทจะต้องมีคลาสที่เกี่ยวข้อง
# Hmm... Doesn't look like there's a class for Int
นอกจากนี้ยังเป็นไปได้ว่าในภาษาของเล่นของเราไม่ใช่ทุกชั้นมีประเภทโดยเฉพาะอย่างยิ่งถ้าไม่ใช่ทุกประเภทของเรามีประเภท มันเป็นเรื่องยากขึ้นเล็กน้อย (แต่ไม่ใช่เป็นไปไม่ได้) ที่จะจินตนาการว่ากฎความสอดคล้องของระบบประเภทใดจะมีลักษณะเช่นถ้านิพจน์บางประเภทมีประเภทและบางส่วนไม่ได้
ยิ่งไปกว่านั้นในภาษาของเล่นของเราการเชื่อมโยงเหล่านี้ไม่จำเป็นต้องไม่ซ้ำกัน เราสามารถเชื่อมโยงสองคลาสกับประเภทเดียวกัน
associate MyCustomStringClass with String
ทีนี้โปรดจำไว้ว่าไม่มีความจำเป็นสำหรับเครื่องพิมพ์ดีดของเราในการติดตามค่าของการแสดงออก (และในกรณีส่วนใหญ่มันจะไม่ทำหรือเป็นไปไม่ได้ที่จะทำเช่นนั้น) ทุกอย่างที่รู้คือป้ายกำกับที่คุณบอก เพื่อเตือนความจำก่อนหน้านี้ตัวพิมพ์ตรวจจับเท่านั้นที่สามารถปฏิเสธคำสั่งได้0 is of type String
เนื่องจากกฎประเภทที่สร้างขึ้นอย่างดุเดือดของเราที่การแสดงออกต้องมีประเภทที่ไม่ซ้ำกันและเราได้ติดป้ายการแสดงออก0
อย่างอื่นแล้ว มันไม่ได้มีความรู้พิเศษใด ๆ 0
ของค่าของ
แล้วประเภทย่อยล่ะ? การพิมพ์ย่อยเป็นชื่อสำหรับกฎทั่วไปในการตรวจสอบชนิดที่ผ่อนคลายกฎอื่น ๆ ที่คุณอาจมี กล่าวคือถ้าA is subtype of B
แล้วทุก typechecker ของคุณเรียกร้องฉลากของก็จะยอมรับB
A
ตัวอย่างเช่นเราอาจทำสิ่งต่อไปนี้สำหรับหมายเลขของเราแทนที่จะเป็นสิ่งที่เราเคยทำก่อนหน้านี้
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
...
การแบ่งคลาสย่อยเป็นชวเลขสำหรับการประกาศคลาสใหม่ที่อนุญาตให้คุณใช้วิธีการและฟิลด์ที่ประกาศไว้ก่อนหน้านี้อีกครั้ง
class ExtendedStringClass is subclass of StringClass:
# We get concatenate and size for free!
def addQuestionMark: ...
เราไม่จำเป็นต้องเชื่อมโยงอินสแตนซ์ของExtendedStringClass
ด้วยString
เหมือนที่เราทำStringClass
มาตั้งแต่หลังจากทั้งหมดเป็นคลาสใหม่ทั้งหมดเราก็ไม่จำเป็นต้องเขียนมากนัก สิ่งนี้จะช่วยให้เราสามารถExtendedStringClass
กำหนดประเภทที่เข้ากันไม่ได้String
จากมุมมองของผู้ตรวจสอบประเภท
ในทำนองเดียวกันเราสามารถตัดสินใจที่จะสร้างชั้นเรียนใหม่ทั้งหมดNewClass
และทำ
associate NewClass with String
ตอนนี้ทุกอินสแตนซ์ของStringClass
สามารถถูกแทนที่ด้วยNewClass
จากมุมมองของเครื่องพิมพ์ดีด
ดังนั้นในทฤษฎีย่อยและ subclassing เป็นสิ่งที่แตกต่างกันโดยสิ้นเชิง แต่ไม่มีภาษาที่ฉันรู้ว่ามีประเภทและคลาสจริง ๆ แล้วทำสิ่งนี้ มาเริ่มต้นแยกภาษาของเราและอธิบายเหตุผลเบื้องหลังการตัดสินใจของเรา
ก่อนอื่นแม้ว่าในทางทฤษฎีแล้วคลาสที่แตกต่างกันอย่างสิ้นเชิงจะได้รับประเภทเดียวกันหรือคลาสอาจได้รับประเภทเดียวกันกับค่าที่ไม่ใช่อินสแตนซ์ของคลาสใด ๆ ก็ตาม แต่สิ่งนี้ทำให้ประโยชน์ของเครื่องพิมพ์ดีดเป็นไปอย่างรุนแรง ตัวตรวจสอบชนิดถูกปล้นความสามารถในการตรวจสอบอย่างมีประสิทธิภาพว่าวิธีการหรือเขตข้อมูลที่คุณโทรภายในนิพจน์นั้นมีอยู่จริงในค่านั้นหรือไม่ซึ่งอาจเป็นการตรวจสอบที่คุณต้องการหากคุณกำลังมีปัญหาในการเล่นพร้อมกับ typechecker ท้ายที่สุดใครจะรู้ว่ามูลค่าที่แท้จริงภายใต้String
ฉลากนั้นคืออะไร มันอาจเป็นสิ่งที่ไม่มีเช่นconcatenate
วิธีเลย!
เอาล่ะเรามากำหนดว่าทุกคลาสจะสร้างชื่อใหม่ให้associate
กับอินสแตนซ์ของคลาสนั้นโดยอัตโนมัติ ที่ช่วยให้เราได้รับการกำจัดassociate
เช่นเดียวกับชื่อที่แตกต่างกันระหว่างและStringClass
String
ด้วยเหตุผลเดียวกันเราอาจต้องการสร้างความสัมพันธ์ย่อยโดยอัตโนมัติระหว่างประเภทของสองคลาสโดยที่หนึ่งเป็นคลาสย่อยของอีกคลาสหนึ่ง หลังจากคลาสย่อยทั้งหมดได้รับการรับรองว่ามีเมธอดและฟิลด์ทั้งหมดที่คลาสพาเรนต์ทำ แต่ตรงกันข้ามไม่เป็นความจริง ดังนั้นในขณะที่คลาสย่อยสามารถส่งผ่านเมื่อใดก็ตามที่คุณต้องการประเภทของคลาสพาเรนต์ประเภทของคลาสพาเรนต์ควรถูกปฏิเสธหากคุณต้องการประเภทของคลาสย่อย
หากคุณรวมสิ่งนี้กับข้อกำหนดที่ผู้ใช้กำหนดค่าทั้งหมดต้องเป็นอินสแตนซ์ของคลาสคุณสามารถis subclass of
ดึงหน้าที่สองเท่าและกำจัดis subtype of
ได้
และนี่ทำให้เราได้รับคุณลักษณะที่ส่วนใหญ่ของภาษา OO ที่พิมพ์แบบคงที่ยอดนิยมใช้ร่วมกัน มีชุดของ "ดั้งเดิม" (เช่นint
, float
ฯลฯ ) ซึ่งไม่เกี่ยวข้องกับชั้นเรียนใด ๆ และไม่ได้กำหนดโดยผู้ใช้ จากนั้นคุณจะมีคลาสที่ผู้ใช้กำหนดเองซึ่งมีชื่อเหมือนกันโดยอัตโนมัติและระบุคลาสย่อยด้วยการพิมพ์ย่อย
หมายเหตุสุดท้ายที่ฉันจะทำคือเกี่ยวกับ clunkiness ของการประกาศแยกต่างหากจากค่า ภาษาส่วนใหญ่ทำให้เกิดการสร้างทั้งสองอย่างเพื่อให้การประกาศประเภทเป็นการประกาศสำหรับการสร้างค่าใหม่ทั้งหมดที่ติดป้ายกำกับด้วยประเภทนั้นโดยอัตโนมัติ ตัวอย่างเช่นการประกาศคลาสโดยทั่วไปทั้งสองจะสร้างประเภทเช่นเดียวกับวิธีการสร้างค่าของประเภทนั้น สิ่งนี้จะกำจัดความบาง clunkiness และในการแสดงตนของ constructors ยังช่วยให้คุณสร้างฉลากที่มีค่ามากมายในประเภทเดียวในจังหวะเดียว