ฉันไม่รู้ว่ามีคำเฉพาะสำหรับปัญหานี้หรือไม่ แต่มีวิธีแก้ไขปัญหาทั่วไปสามประเภท:
- หลีกเลี่ยงประเภทคอนกรีตในความโปรดปรานของการจัดส่งแบบไดนามิก
- อนุญาตให้ใช้พารามิเตอร์ประเภทตัวยึดตำแหน่งในข้อ จำกัด ประเภท
- หลีกเลี่ยงพารามิเตอร์ประเภทโดยใช้ประเภทตระกูล / ประเภทที่เกี่ยวข้อง
และแน่นอนทางออกเริ่มต้น: สะกดคำพารามิเตอร์เหล่านั้นทั้งหมด
หลีกเลี่ยงประเภทคอนกรีต
คุณได้กำหนดIterable
อินเทอร์เฟซเป็น:
interface <Element> Iterable<T: Iterator<Element>> {
getIterator(): T
}
สิ่งนี้จะช่วยให้ผู้ใช้งานได้รับพลังงานสูงสุดต่อเนื่องเพราะพวกมันได้ชนิดT
ของตัววนซ้ำที่แน่นอน สิ่งนี้ยังอนุญาตให้คอมไพเลอร์ใช้การเพิ่มประสิทธิภาพมากขึ้นเช่น inlining
อย่างไรก็ตามถ้าIterator<E>
เป็นอินเทอร์เฟซที่จัดส่งแบบไดนามิกดังนั้นไม่จำเป็นต้องทราบประเภทที่เป็นรูปธรรม นี่คือวิธีแก้ปัญหาที่ Java ใช้ อินเตอร์เฟสจะถูกเขียนเป็น:
interface Iterable<Element> {
getIterator(): Iterator<Element>
}
รูปแบบที่น่าสนใจของรูปแบบนี้คือimpl Trait
ไวยากรณ์ของ Rust ซึ่งช่วยให้คุณประกาศฟังก์ชันที่มีรูปแบบการส่งคืนที่เป็นนามธรรม แต่ทราบว่ารูปแบบคอนกรีตจะเป็นที่รู้จักในไซต์การโทร สิ่งนี้จะทำงานคล้ายกับพารามิเตอร์ประเภทโดยนัย
อนุญาตให้ใช้พารามิเตอร์ประเภทตัวยึดตำแหน่ง
Iterable
อินเตอร์เฟซที่ไม่จำเป็นต้องรู้เกี่ยวกับประเภทองค์ประกอบดังนั้นมันอาจจะเป็นไปได้ที่จะเขียนนี้เป็น:
interface Iterable<T: Iterator<_>> {
getIterator(): T
}
โดยที่T: Iterator<_>
เป็นการแสดงออกถึงข้อ จำกัด “ T คือตัววนซ้ำใด ๆ โดยไม่คำนึงถึงประเภทองค์ประกอบ” เพิ่มเติมอย่างจริงจังเราสามารถแสดงนี้ว่า“มีอยู่บางชนิดElement
เพื่อให้T
เป็นIterator<Element>
” Element
โดยไม่ต้องทราบชนิดที่เป็นรูปธรรมใด ซึ่งหมายความว่าการแสดงออกประเภทIterator<_>
ไม่ได้อธิบายประเภทที่เกิดขึ้นจริงและสามารถใช้เป็นข้อ จำกัด ประเภท
ใช้ตระกูลตระกูล / ประเภทที่เกี่ยวข้อง
เช่นใน C ++ ประเภทอาจมีสมาชิกประเภท std::vector::value_type
นี้เป็นที่นิยมใช้ทั่วห้องสมุดมาตรฐานเช่น สิ่งนี้ไม่ได้แก้ปัญหาพารามิเตอร์ประเภทในทุกสถานการณ์ แต่เนื่องจากประเภทอาจอ้างถึงประเภทอื่น ๆ พารามิเตอร์ประเภทเดี่ยวจึงสามารถอธิบายตระกูลทั้งหมดของประเภทที่เกี่ยวข้องได้
มานิยามกัน:
interface Iterator {
type ElementType
fn next(): ElementType
}
interface Iterable {
type IteratorType: Iterator
fn getIterator(): IteratorType
}
แล้ว:
class Vec<Element> implement Iterable {
type IteratorType = VecIterator<Element>
fn getIterator(): IteratorType { ... }
}
class VecIterator<T> implements Iterator {
type ElementType = T
fn next(): ElementType { ... }
}
สิ่งนี้ดูมีความยืดหยุ่นมาก แต่โปรดทราบว่านี่อาจทำให้ยากที่จะแสดงข้อ จำกัด ประเภท เช่นที่เขียนไว้Iterable
ไม่ได้บังคับประเภทองค์ประกอบตัววนซ้ำใด ๆ และเราอาจต้องการประกาศinterface Iterator<T>
แทน และตอนนี้คุณกำลังจัดการกับแคลคูลัสชนิดที่ค่อนข้างซับซ้อน เป็นเรื่องง่ายมากที่จะทำให้ระบบประเภทดังกล่าวไม่สามารถตัดสินใจได้ (หรืออาจเป็นแบบนั้นอยู่แล้ว?)
โปรดทราบว่าประเภทที่เชื่อมโยงนั้นสะดวกสบายมากเป็นค่าเริ่มต้นสำหรับพารามิเตอร์ประเภท เช่นสมมติว่าIterable
อินเทอร์เฟซต้องการพารามิเตอร์ชนิดแยกต่างหากสำหรับประเภทองค์ประกอบซึ่งโดยปกติแล้วจะไม่เหมือนกันกับชนิดองค์ประกอบตัววนซ้ำและเรามีพารามิเตอร์ตัวยึดตำแหน่งอาจเป็นไปได้ที่จะพูดว่า:
interface Iterable<T: Iterator<_>, Element = T::Element> {
...
}
อย่างไรก็ตามนั่นเป็นเพียงคุณสมบัติการยศาสตร์ภาษาและไม่ทำให้ภาษามีประสิทธิภาพมากขึ้น
ระบบประเภทนั้นยากดังนั้นจึงเป็นเรื่องดีที่จะดูว่าอะไรดีและใช้งานไม่ได้กับภาษาอื่น
เช่นลองอ่านบทลักษณะขั้นสูงใน Rust Book ซึ่งพูดถึงประเภทที่เกี่ยวข้อง แต่โปรดทราบว่ามีบางจุดที่สนับสนุนประเภทที่เกี่ยวข้องแทนที่จะใช้เฉพาะเรื่องทั่วไปเพราะภาษานั้นไม่ได้มีฟีเจอร์ย่อยและคุณลักษณะแต่ละอย่างสามารถนำไปใช้ได้มากที่สุดหนึ่งครั้งต่อประเภท Ie Rust คุณลักษณะไม่ใช่อินเทอร์เฟซเหมือน Java
ระบบประเภทที่น่าสนใจอื่น ๆ ได้แก่ Haskell ที่มีนามสกุลภาษาต่าง ๆ โมดูล / ฟังก์ชั่น OCaml เป็นประเภทตระกูลที่ค่อนข้างธรรมดาโดยไม่รบกวนโดยตรงกับวัตถุหรือประเภทที่กำหนดพารามิเตอร์ Java โดดเด่นสำหรับข้อ จำกัด ในระบบชนิดของมันเช่น generics ที่มีการลบประเภทและไม่มี generics มากกว่าประเภทค่า C # มีลักษณะคล้าย Java แต่สามารถหลีกเลี่ยงข้อ จำกัด เหล่านี้ส่วนใหญ่ในราคาที่เพิ่มความซับซ้อนในการใช้งาน Scala พยายามรวม C--style generics เข้ากับ typeclasses สไตล์ Haskell ด้านบนของแพลตฟอร์ม Java เทมเพลตแบบง่ายที่หลอกลวงของ C ++ นั้นมีการศึกษามาอย่างดี แต่ไม่เหมือนกับการใช้งานทั่วไป
นอกจากนี้ยังควรดูที่ไลบรารีมาตรฐานของภาษาเหล่านี้ (โดยเฉพาะคอลเล็กชันไลบรารีมาตรฐานเช่นรายการหรือตารางแฮช) เพื่อดูว่ารูปแบบใดที่ใช้กันโดยทั่วไป เช่น C ++ มีระบบที่ซับซ้อนของความสามารถในการวนซ้ำที่แตกต่างกันและ Scala เข้ารหัสความสามารถในการเก็บรวบรวมที่ละเอียดเป็นคุณลักษณะ บางครั้งอินเทอร์เฟซไลบรารีมาตรฐานของ Java จะไม่ปลอดภัยเช่นIterator#remove()
แต่สามารถใช้คลาสที่ซ้อนกันเป็นชนิดที่เกี่ยวข้อง (เช่นMap.Entry
)