Scala: บทคัดย่อประเภทเทียบกับยาชื่อสามัญ


250

ผมอ่านทัวร์ของ Scala: นามธรรมประเภท เมื่อใดควรใช้ประเภทนามธรรม

ตัวอย่างเช่น,

abstract class Buffer {
  type T
  val element: T
}

ค่อนข้างทั่วไปเช่นนั้น

abstract class Buffer[T] {
  val element: T
}

คำตอบ:


257

คุณมีมุมมองที่ดีเกี่ยวกับปัญหานี้ที่นี่:

วัตถุประสงค์ของระบบประเภทของสกาล่า
การสนทนากับ Martin Odersky ตอนที่สาม
โดย Bill Venners และ Frank Sommers (18 พฤษภาคม 2009)

Update (October2009): สิ่งที่ตามมาด้านล่างมีการแสดงจริงในบทความใหม่นี้โดย Bill Venners:
สมาชิกประเภทบทคัดย่อกับพารามิเตอร์ประเภททั่วไปใน Scala (ดูสรุปที่ส่วนท้าย)


(นี่คือสารสกัดที่เกี่ยวข้องของการสัมภาษณ์ครั้งแรกพฤษภาคม 2009 เน้นเหมือง)

หลักการทั่วไป

มีสองแนวคิดเกี่ยวกับสิ่งที่เป็นนามธรรมเสมอ:

  • การกำหนดพารามิเตอร์และ
  • สมาชิกที่เป็นนามธรรม

ใน Java คุณมีทั้งสองอย่าง แต่มันก็ขึ้นอยู่กับสิ่งที่คุณเป็นนามธรรม
ใน Java คุณมีวิธีนามธรรม แต่คุณไม่สามารถส่งเมธอดเป็นพารามิเตอร์ได้
คุณไม่มีฟิลด์บทคัดย่อ แต่คุณสามารถส่งค่าเป็นพารามิเตอร์ได้
และในทำนองเดียวกันคุณไม่มีสมาชิกประเภท abstract แต่คุณสามารถระบุประเภทเป็นพารามิเตอร์ได้
ดังนั้นใน Java คุณมีทั้งสามอย่างนี้ แต่มีความแตกต่างเกี่ยวกับหลักการที่เป็นนามธรรมที่คุณสามารถใช้กับสิ่งต่าง ๆ และคุณสามารถยืนยันได้ว่าความแตกต่างนี้ค่อนข้างมีข้อ จำกัด

เส้นทางสกาลา

เราตัดสินใจที่จะมีหลักการก่อสร้างเดียวกันสำหรับทั้งสามประเภทของสมาชิก
ดังนั้นคุณสามารถมีฟิลด์บทคัดย่อรวมถึงพารามิเตอร์ค่า
คุณสามารถส่งเมธอด (หรือ "ฟังก์ชั่น") เป็นพารามิเตอร์หรือคุณสามารถสรุปได้
คุณสามารถระบุประเภทเป็นพารามิเตอร์หรือคุณสามารถสรุปได้
และสิ่งที่เราได้รับแนวความคิดก็คือเราสามารถจำลองแบบหนึ่งในแง่ของสิ่งอื่น อย่างน้อยที่สุดในหลักการเราสามารถแสดงการกำหนดพารามิเตอร์ทุกประเภทเป็นรูปแบบของนามธรรมเชิงวัตถุ ดังนั้นในแง่หนึ่งคุณอาจพูดได้ว่า Scala เป็นภาษาที่มีฉากและสมบูรณ์มากกว่า

ทำไม?

โดยเฉพาะอย่างยิ่งสิ่งที่เป็นนามธรรมซื้อคุณคือการรักษาที่ดีสำหรับปัญหาความแปรปรวนร่วมที่เราพูดถึงก่อน
ปัญหามาตรฐานหนึ่งปัญหาที่เกิดขึ้นมานานแล้วคือปัญหาของสัตว์และอาหาร
จิ๊กซอว์จะต้องมีชั้นเรียนAnimalด้วยวิธีการeatซึ่งกินอาหารบางอย่าง
ปัญหาคือถ้าเรา subclass สัตว์และมีคลาสเช่นวัวแล้วพวกเขาจะกินหญ้าเท่านั้นและไม่ใช่อาหารโดยพลการ ยกตัวอย่างเช่นวัวไม่สามารถกินปลาได้
สิ่งที่คุณต้องการคือสามารถพูดได้ว่าวัวมีวิธีกินที่กินหญ้าเท่านั้นและไม่ใช่สิ่งอื่น
ที่จริงแล้วคุณไม่สามารถทำเช่นนั้นใน Java เพราะปรากฎว่าคุณสามารถสร้างสถานการณ์ที่ไม่มั่นคงเช่นปัญหาของการกำหนดผลไม้ให้กับตัวแปร Apple ที่ฉันพูดถึงก่อนหน้านี้

คำตอบก็คือคุณเพิ่มประเภทนามธรรมลงในระดับสัตว์
คุณพูดว่าคลาสสัตว์ใหม่ของฉันมีประเภทSuitableFoodที่ฉันไม่รู้
ดังนั้นมันจึงเป็นแบบนามธรรม คุณไม่ให้ประเภทของการใช้งาน แล้วคุณมีวิธีการที่กินเท่านั้นeat จากนั้นในชั้นเรียนผมจะบอกว่าตกลงฉันมีวัวซึ่งขยายชั้นเรียนและ ดังนั้นประเภทนามธรรมให้ความคิดนี้ของชนิดใน superclass ที่ผมไม่ทราบซึ่งผมแล้วกรอกข้อมูลในภายหลังใน subclasses กับสิ่งที่ฉันจะรู้SuitableFood
CowAnimalCow type SuitableFood equals Grass

เหมือนกับการกำหนดพารามิเตอร์หรือไม่

แน่นอนคุณสามารถ คุณสามารถกำหนดสัตว์ชั้นเรียนด้วยอาหารประเภทที่มันกินได้
แต่ในทางปฏิบัติเมื่อคุณทำอย่างนั้นกับสิ่งที่แตกต่างกันจำนวนมากก็จะนำไปสู่การระเบิดของพารามิเตอร์และมักจะมีอะไรมากขึ้นในขอบเขตของพารามิเตอร์
ที่ ECOOP ปี 1998 Kim Bruce, Phil Wadler และฉันมีกระดาษที่เราแสดงให้เห็นว่าเมื่อคุณเพิ่มจำนวนของสิ่งที่คุณไม่ทราบโปรแกรมทั่วไปจะเพิ่มขึ้นเป็นสองเท่า
ดังนั้นมีเหตุผลที่ดีมากที่จะไม่ทำพารามิเตอร์ แต่มีสมาชิกที่เป็นนามธรรมเหล่านี้เพราะพวกเขาไม่ได้ให้กำลังสองนี้แก่คุณ


thatismattถามในความคิดเห็น:

คุณคิดว่าต่อไปนี้เป็นบทสรุปที่ยุติธรรม:

  • ประเภทนามธรรมถูกนำมาใช้ในความสัมพันธ์ 'has-a' หรือ 'uses-a' (เช่น a Cow eats Grass)
  • โดยทั่วไปแล้วความสัมพันธ์แบบ 'ทั่วไป' (เช่นList of Ints)

ฉันไม่แน่ใจว่าความสัมพันธ์นั้นแตกต่างกันระหว่างการใช้ประเภทนามธรรมหรือชื่อสามัญ สิ่งที่แตกต่างคือ:

  • วิธีการใช้งานและ
  • วิธีจัดการขอบเขตของพารามิเตอร์

เพื่อให้เข้าใจถึงสิ่งที่มาร์ตินพูดเกี่ยวกับเมื่อมันมาถึง "การระเบิดของพารามิเตอร์และมักจะมีอะไรมากขึ้นในขอบเขตของพารามิเตอร์ " และการเติบโต quadratically เมื่อประเภทนามธรรมเป็นรูปแบบการใช้ยาชื่อสามัญคุณสามารถพิจารณากระดาษ " Scalable ตัวแทนที่เป็นนามธรรม "เขียนโดย ... Martin Odersky และ Matthias Zenger สำหรับ OOPSLA 2005 อ้างอิงในสิ่งพิมพ์ของโครงการ Palcom (เสร็จสิ้นในปี 2550)

สารสกัดที่เกี่ยวข้อง

คำนิยาม

สมาชิกประเภทที่เป็นนามธรรมให้วิธีที่ยืดหยุ่นในการทำให้เป็นนามธรรมเหนือส่วนประกอบประเภทที่เป็นรูปธรรม
ประเภทนามธรรมสามารถซ่อนข้อมูลเกี่ยวกับภายในของส่วนประกอบซึ่งคล้ายกับการใช้งานในลายเซ็นSML ในเฟรมเวิร์กเชิงวัตถุที่คลาสสามารถขยายได้โดยการสืบทอดพวกมันอาจถูกใช้เป็นวิธีการกำหนดพารามิเตอร์ที่ยืดหยุ่น (มักเรียกว่า polymorphism ตระกูลดูรายการบันทึกทางเว็บนี้เป็นต้นและบทความที่เขียนโดยEric Ernst )

(หมายเหตุ: Family polymorphism ถูกเสนอสำหรับภาษาเชิงวัตถุเป็นวิธีแก้ปัญหาเพื่อสนับสนุนคลาส recursive ที่ใช้ร่วมกันได้ แต่ปลอดภัยชนิดที่ใช้ร่วมกัน
ความคิดหลักของ polymorphism ในครอบครัวคือแนวคิดของครอบครัวซึ่งใช้เพื่อจัดกลุ่มคลาสเรียกซ้ำกัน)

สิ่งที่เป็นนามธรรมประเภท

abstract class MaxCell extends AbsCell {
type T <: Ordered { type O = T }
def setMax(x: T) = if (get < x) set(x)
}

ที่นี่ประกาศประเภทของ T จะ จำกัด โดยประเภทบนผูกพัน{ type O = T }ซึ่งประกอบด้วยชื่อชั้นสั่งซื้อและการปรับแต่ง
ถูกผูกไว้บนข้อกำหนดด้านความเชี่ยวชาญของ T ใน subclasses เพื่อเชื้อของผู้สั่งซื้อที่ประเภทสมาชิกของO เนื่องจากข้อ จำกัด นี้เมธอดของคลาส Ordered จึงรับประกันว่าจะสามารถใช้ได้กับผู้รับและอาร์กิวเมนต์ประเภท T ตัวอย่างแสดงให้เห็นว่าสมาชิกประเภทที่ถูกผูกมัดอาจปรากฏเป็นส่วนหนึ่งของขอบเขต (เช่น Scala รองรับความหลากหลายรูปแบบ F-bounded )equals T
<

(หมายเหตุจาก Peter Canning, William Cook, Walter Hill, Walter Olthoff paper:
ปริมาณที่ จำกัด ได้รับการแนะนำโดย Cardelli และ Wegner เป็นวิธีการพิมพ์ฟังก์ชั่นที่ทำงานเหมือนกันทุกประเภทย่อยที่กำหนด
พวกเขากำหนดรูปแบบ "วัตถุ" ที่เรียบง่าย และใช้ขอบเขตปริมาณเพื่อตรวจสอบประเภทของฟังก์ชั่นที่เหมาะสมกับวัตถุทั้งหมดที่มีชุดของ "คุณสมบัติ" ที่ระบุ
การนำเสนอที่เป็นจริงมากขึ้นของภาษาเชิงวัตถุจะช่วยให้วัตถุที่เป็นองค์ประกอบของประเภทกำหนดซ้ำ
ในบริบทนี้ การหาปริมาณไม่ตรงตามวัตถุประสงค์อีกต่อไปมันเป็นเรื่องง่ายที่จะหาฟังก์ชั่นที่เหมาะสมกับวัตถุทั้งหมดที่มีชุดวิธีการที่ระบุ แต่ไม่สามารถพิมพ์ในระบบ Cardelli-Wegner
เพื่อเป็นพื้นฐานสำหรับฟังก์ชั่น polymorphic ที่พิมพ์ในภาษาเชิงวัตถุเราแนะนำปริมาณ F-bounded)

สองเหรียญที่เหมือนกัน

มีสองรูปแบบหลักของนามธรรมในภาษาโปรแกรม:

  • การกำหนดพารามิเตอร์และ
  • สมาชิกที่เป็นนามธรรม

รูปแบบแรกเป็นเรื่องปกติสำหรับภาษาที่ใช้งานได้ในขณะที่รูปแบบที่สองมักใช้ในภาษาเชิงวัตถุ

ตามเนื้อผ้าจาวาสนับสนุนการกำหนดพารามิเตอร์สำหรับค่าและสมาชิก abstraction สำหรับการดำเนินการ Java 5.0 ล่าสุดที่มี generics รองรับการกำหนดพารามิเตอร์ด้วยเช่นกัน

อาร์กิวเมนต์สำหรับการรวม generics ใน Scala นั้นมีสองแบบ:

  • อย่างแรกการเข้ารหัสในรูปแบบนามธรรมนั้นไม่ใช่เรื่องง่ายที่จะต้องทำด้วยมือ นอกจากการสูญเสียความกระชับแล้วยังมีปัญหาความขัดแย้งของชื่ออุบัติเหตุระหว่างชื่อประเภทนามธรรมที่เลียนแบบพารามิเตอร์ประเภท

  • ประการที่สองประเภททั่วไปและนามธรรมมักจะให้บริการบทบาทที่แตกต่างในโปรแกรมสกาล่า

    • ข้อมูลทั่วไปมักจะใช้เมื่อต้องการเพียงแค่การเริ่มชนิดในขณะที่
    • ประเภทนามธรรมมักจะใช้เมื่อหนึ่งต้องดูที่ประเภทนามธรรมจากรหัสลูกค้า
      หลังเกิดขึ้นโดยเฉพาะในสองสถานการณ์:
    • หนึ่งอาจต้องการซ่อนคำจำกัดความที่แน่นอนของสมาชิกประเภทจากรหัสลูกค้าเพื่อรับการห่อหุ้มที่รู้จักจากระบบโมดูลสไตล์ SML
    • หรืออาจต้องการแทนที่ประเภท covariantly ในคลาสย่อยเพื่อให้ได้ polymorphism ในครอบครัว

ในระบบที่มีความแตกต่างขอบเขตการเขียนประเภทนามธรรมเข้า generics อาจนำมาซึ่งการขยายตัวของกำลังสองประเภทขอบเขต


อัปเดตตุลาคม 2552

บทคัดย่อประเภทสมาชิกเมื่อเทียบกับพารามิเตอร์ประเภททั่วไปใน Scala (Bill Venners)

(เน้นที่เหมือง)

การสังเกตของฉันเกี่ยวกับสมาชิกประเภทนามธรรมคือว่าพวกเขาเป็นตัวเลือกที่ดีกว่าพารามิเตอร์ประเภททั่วไปเมื่อ:

  • คุณต้องการที่จะให้คนผสมในคำจำกัดความของประเภทที่ผ่านลักษณะ
  • คุณคิดว่าชัดเจนถึงชื่อประเภทสมาชิกเมื่อมันถูกกำหนดไว้จะช่วยให้การอ่านรหัส

ตัวอย่าง:

หากคุณต้องการผ่านการติดตั้งวัตถุที่แตกต่างกันสามแบบเข้าสู่การทดสอบคุณจะสามารถทำได้ แต่คุณจะต้องระบุสามประเภทหนึ่งประเภทสำหรับแต่ละพารามิเตอร์ ดังนั้นฉันจึงใช้วิธีการพิมพ์พารามิเตอร์คลาสชุดของคุณอาจมีลักษณะเช่นนี้:

// Type parameter version
class MySuite extends FixtureSuite3[StringBuilder, ListBuffer, Stack] with MyHandyFixture {
  // ...
}

ในขณะที่วิธีการแบบสมาชิกจะมีลักษณะเช่นนี้:

// Type member version
class MySuite extends FixtureSuite3 with MyHandyFixture {
  // ...
}

ความแตกต่างเล็กน้อยอีกอย่างหนึ่งระหว่างสมาชิกประเภทนามธรรมและพารามิเตอร์ประเภททั่วไปคือเมื่อระบุพารามิเตอร์ประเภททั่วไปผู้อ่านรหัสจะไม่เห็นชื่อของพารามิเตอร์ประเภท ดังนั้นมีคนเห็นรหัสบรรทัดนี้:

// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] with StringBuilderFixture {
  // ...
}

พวกเขาไม่รู้ว่าชื่อของพารามิเตอร์ประเภทที่ระบุเป็น StringBuilder คืออะไรโดยไม่ต้องค้นหา ในขณะที่ชื่อของพารามิเตอร์ type อยู่ที่นั่นในรหัสในแนวทางสมาชิกแบบนามธรรม:

// Type member version
class MySuite extends FixtureSuite with StringBuilderFixture {
  type FixtureParam = StringBuilder
  // ...
}

ในกรณีหลังผู้อ่านรหัสจะเห็นว่าStringBuilderเป็นชนิด "พารามิเตอร์การติดตั้ง"
พวกเขายังคงต้องคิดออกว่า "พารามิเตอร์การติดตั้ง" หมายถึง แต่อย่างน้อยพวกเขาสามารถได้รับชื่อของประเภทโดยไม่ต้องดูในเอกสารประกอบ


61
ฉันควรจะได้รับคะแนนกรรมด้วยการตอบคำถามสกาล่าเมื่อคุณมาทำสิ่งนี้ได้อย่างไร? :-)
Daniel C. Sobral

7
สวัสดีแดเนียล: ฉันคิดว่าจะต้องมีตัวอย่างที่เป็นรูปธรรมเพื่อแสดงให้เห็นถึงข้อได้เปรียบของประเภทนามธรรมมากกว่าการตั้งพาราเมทริก การโพสต์บางอย่างในกระทู้นี้จะเป็นการเริ่มต้นที่ดี;) ฉันรู้ว่าฉันจะโหวตว่า
VonC

1
คุณคิดว่าต่อไปนี้เป็นบทสรุปที่เป็นธรรม: มีการใช้ประเภทนามธรรมในความสัมพันธ์แบบ 'has-a' หรือ 'uses-a' (เช่น Cow eats Grass) ซึ่งโดยทั่วไปแล้วความสัมพันธ์ของข้อมูลทั่วไป (เช่น List of Ints)
thatismatt

ฉันไม่แน่ใจว่าความสัมพันธ์นั้นแตกต่างกันระหว่างการใช้ประเภทนามธรรมหรือชื่อสามัญ สิ่งที่แตกต่างคือวิธีการใช้งานและวิธีจัดการขอบเขตของพารามิเตอร์ เพิ่มเติมในคำตอบของฉันในอีกสักครู่
VonC

1
หมายเหตุถึงตัวเอง: ดูโพสต์บล็อกพฤษภาคม 2010 นี้: รายวัน--scala.blogspot.com/2010/05/ …
VonC

37

ฉันมีคำถามเดียวกันเมื่อฉันอ่านเกี่ยวกับสกาล่า

ข้อได้เปรียบของการใช้ยาชื่อสามัญคือคุณกำลังสร้างตระกูลประเภท ไม่มีใครจะต้องซับคลาสBuffer-they ก็สามารถใช้Buffer[Any], Buffer[String]ฯลฯ

หากคุณใช้ประเภทนามธรรมผู้คนจะถูกบังคับให้สร้างคลาสย่อย คนจะต้องเรียนเหมือนAnyBuffer, StringBufferฯลฯ

คุณต้องตัดสินใจว่าอะไรดีกว่าสำหรับความต้องการเฉพาะของคุณ


18
mmm thins ได้รับการปรับปรุงอย่างมากในหน้านี้คุณเพียงแค่ต้องการBuffer { type T <: String }หรือBuffer { type T = String }ขึ้นอยู่กับความต้องการของคุณ
Eduardo Pareja Tobes

21

คุณสามารถใช้ประเภทนามธรรมร่วมกับพารามิเตอร์ประเภทเพื่อสร้างแม่แบบกำหนดเอง

สมมติว่าคุณจำเป็นต้องสร้างรูปแบบที่มีลักษณะการเชื่อมต่อที่สาม:

trait AA[B,C]
trait BB[C,A]
trait CC[A,B]

ในทางที่ข้อโต้แย้งที่กล่าวถึงในพารามิเตอร์ประเภทคือ AA, BB, CC เองด้วยความเคารพ

คุณอาจมีรหัสบางประเภท:

trait AA[B<:BB[C,AA[B,C]],C<:CC[AA[B,C],B]]
trait BB[C<:CC[A,BB[C,A]],A<:AA[BB[C,A],C]]
trait CC[A<:AA[B,CC[A,B]],B<:BB[CC[A,B],A]]

ซึ่งจะไม่ทำงานในวิธีที่ง่ายนี้เพราะพันธบัตรพารามิเตอร์ประเภท คุณต้องทำให้มันแปรปรวนร่วมเพื่อสืบทอดอย่างถูกต้อง

trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]]
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]]
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]]

ตัวอย่างนี้จะรวบรวม แต่มีข้อกำหนดที่เข้มงวดเกี่ยวกับกฎความแปรปรวนและไม่สามารถใช้ได้ในบางโอกาส

trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]] {
  def forth(x:B):C
  def back(x:C):B
}
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]] {
  def forth(x:C):A
  def back(x:A):C
}
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]] {
  def forth(x:A):B
  def back(x:B):A
}

คอมไพเลอร์จะคัดค้านข้อผิดพลาดการตรวจสอบความแปรปรวนจำนวนมาก

ในกรณีนี้คุณอาจรวบรวมข้อกำหนดประเภททั้งหมดในลักษณะเพิ่มเติมและกำหนดลักษณะอื่น ๆ

//one trait to rule them all
trait OO[O <: OO[O]] { this : O =>
  type A <: AA[O]
  type B <: BB[O]
  type C <: CC[O]
}
trait AA[O <: OO[O]] { this : O#A =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:B):C
  def right(r:C):B = r.left(this)
  def join(l:B, r:C):A
  def double(l:B, r:C):A = this.join( l.join(r,this), r.join(this,l) )
}
trait BB[O <: OO[O]] { this : O#B =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:C):A
  def right(r:A):C = r.left(this)
  def join(l:C, r:A):B
  def double(l:C, r:A):B = this.join( l.join(r,this), r.join(this,l) )
}
trait CC[O <: OO[O]] { this : O#C =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:A):B
  def right(r:B):A = r.left(this)
  def join(l:A, r:B):C
  def double(l:A, r:B):C = this.join( l.join(r,this), r.join(this,l) )
}

ตอนนี้เราสามารถเขียนรูปแบบที่เป็นรูปธรรมสำหรับรูปแบบที่อธิบายกำหนดซ้ายและเข้าร่วมวิธีการในชั้นเรียนทั้งหมดและได้รับสิทธิและสองครั้งฟรี

class ReprO extends OO[ReprO] {
  override type A = ReprA
  override type B = ReprB
  override type C = ReprC
}
case class ReprA(data : Int) extends AA[ReprO] {
  override def left(l:B):C = ReprC(data - l.data)
  override def join(l:B, r:C) = ReprA(l.data + r.data)
}
case class ReprB(data : Int) extends BB[ReprO] {
  override def left(l:C):A = ReprA(data - l.data)
  override def join(l:C, r:A):B = ReprB(l.data + r.data)
}
case class ReprC(data : Int) extends CC[ReprO] {
  override def left(l:A):B = ReprB(data - l.data)
  override def join(l:A, r:B):C = ReprC(l.data + r.data)
}

ดังนั้นทั้ง abstract type และพารามิเตอร์ type ใช้สำหรับการสร้าง abstractions พวกเขาทั้งคู่มีจุดอ่อนและจุดแข็ง ประเภทนามธรรมมีความเฉพาะเจาะจงมากขึ้นและสามารถอธิบายโครงสร้างประเภทใดก็ได้ แต่มีความละเอียดและจำเป็นต้องระบุอย่างชัดเจน พารามิเตอร์ประเภทสามารถสร้างกลุ่มประเภทได้ทันที แต่ให้ความกังวลเพิ่มเติมเกี่ยวกับการสืบทอดและขอบเขตชนิด

พวกเขาให้ความร่วมมือซึ่งกันและกันและสามารถใช้ร่วมกันในการสร้าง abstractions ที่ซับซ้อนซึ่งไม่สามารถแสดงออกได้ด้วยสิ่งใดสิ่งหนึ่งเท่านั้น


0

ฉันคิดว่ามันไม่แตกต่างกันมากที่นี่ สมาชิกประเภทนามธรรมสามารถมองเห็นเป็นประเภทที่มีอยู่เพียงซึ่งคล้ายกับประเภทบันทึกในภาษาการทำงานอื่น ๆ

ตัวอย่างเช่นเรามี:

class ListT {
  type T
  ...
}

และ

class List[T] {...}

จากนั้นก็เป็นเพียงเช่นเดียวกับListT List[_]ความเชื่อมั่นของสมาชิกประเภทคือเราสามารถใช้คลาสที่ไม่มีประเภทชัดเจนและหลีกเลี่ยงพารามิเตอร์ประเภทมากเกินไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.