การลบประเภทของ Java มีประโยชน์อย่างไร?


98

วันนี้ฉันอ่านทวีตที่กล่าวว่า:

เป็นเรื่องตลกเมื่อผู้ใช้ Java บ่นเกี่ยวกับการลบประเภทซึ่งเป็นสิ่งเดียวที่ Java มีสิทธิ์ในขณะที่เพิกเฉยต่อสิ่งที่ผิดพลาดทั้งหมด

ดังนั้นคำถามของฉันคือ:

มีประโยชน์จากการลบประเภทของ Java หรือไม่? รูปแบบทางเทคนิคหรือการเขียนโปรแกรมมีประโยชน์อะไรบ้าง (อาจ) นำเสนอนอกเหนือจากการกำหนดลักษณะการใช้งาน JVM สำหรับความเข้ากันได้แบบย้อนกลับและประสิทธิภาพรันไทม์


6
หากคุณไม่ต้องการให้คำถามของคุณปิดเป็น "ตามความคิดเห็น" คุณควรถามคำถามที่แตกต่างออกไป (เช่น "ข้อดีทางเทคนิคของการลบประเภท" คืออะไร) ยังดีกว่าถามทวีตเตอร์ว่าเขาพูดอะไรจริงๆ
Stephen C

3
ลบ "เหตุใดจึงเป็นสิ่งที่ดี" "สิ่งที่ดี" เป็นลักษณะอัตนัยอย่างเห็นได้ชัดและ (เห็นได้ชัด) ทำให้เกิดคำถามที่ว่าการลบประเภทเป็นสิ่งที่ดี ... ซึ่งเป็นที่ถกเถียงกัน
Stephen C

3
พระเจ้าช่วยเราหากไซต์นี้เสื่อมเสียคำถามเกี่ยวกับทุกสิ่งที่เคยโพสต์บน Twitter อย่างน้อยก็อ้างอิงแหล่งที่มาที่น่าเชื่อถือสำหรับคำถามของคุณ
Marquis of Lorne

21
ทำไมคำถามจึงต้องมาจาก "แหล่งที่มาที่มีชื่อเสียง"? นั่นเป็นเรื่องโง่
vertti

4
ปัญหาส่วนใหญ่ที่นี่ไม่ได้มาจากแหล่งใด ๆ ยกเว้นผู้ถามเป็นเจ้าของปัญหาเกี่ยวกับซอฟต์แวร์ / ภาษา / อะไรที่เฉพาะเจาะจงและโชคดีที่มีผู้เชี่ยวชาญจำนวนมากที่ "เสียเวลา" ในการช่วยเหลือคนเหล่านี้
vertti

คำตอบ:


90

ประเภทการลบเป็นสิ่งที่ดี

มาติดตามข้อเท็จจริงกันเถอะ

คำตอบมากมายในตอนนี้เกี่ยวข้องกับผู้ใช้ Twitter มากเกินไป การให้ความสำคัญกับข้อความไม่ใช่ผู้ส่งสารจะเป็นประโยชน์ มีข้อความที่ค่อนข้างสอดคล้องกับข้อความที่ตัดตอนมาที่กล่าวถึงจนถึงตอนนี้:

เป็นเรื่องตลกเมื่อผู้ใช้ Java บ่นเกี่ยวกับการลบประเภทซึ่งเป็นสิ่งเดียวที่ Java มีสิทธิ์ในขณะที่เพิกเฉยต่อสิ่งที่ผิดพลาดทั้งหมด

ฉันได้รับผลประโยชน์มากมาย (เช่นค่าพาราเมตริก) และค่าใช้จ่าย (ค่าใช้จ่ายที่ถูกกล่าวหาคือขีด จำกัด ของจินตนาการ)

new T เป็นโปรแกรมที่เสีย มันเป็นไอโซมอร์ฟิกสำหรับคำกล่าวอ้าง "ข้อเสนอทั้งหมดเป็นจริง" ฉันไม่ใหญ่ในเรื่องนี้

เป้าหมาย: โปรแกรมที่เหมาะสม

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

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

การใช้ระบบประเภทเพื่อการให้เหตุผล

เป้าหมายเหล่านี้สามารถแก้ไขได้อย่างดีตามระบบประเภท นี้เป็นที่ชัดเจนโดยเฉพาะอย่างยิ่งเพราะแกงโฮเวิร์ดจดหมาย การโต้ตอบนี้มักจะแสดงด้วยการเปรียบเทียบดังต่อไปนี้ประเภทต่างๆเป็นไปตามโปรแกรมตามทฤษฎีบทมีไว้เพื่อพิสูจน์

การติดต่อนี้ค่อนข้างลึกซึ้ง เราสามารถใช้นิพจน์เชิงตรรกะและแปลผ่านการโต้ตอบกับประเภท ถ้าเรามีโปรแกรมที่มีลายเซ็นประเภทเดียวกับที่คอมไพล์เราได้พิสูจน์แล้วว่านิพจน์เชิงตรรกะนั้นเป็นจริงในระดับสากล (tautology) เนื่องจากการติดต่อเป็นสองทาง การเปลี่ยนแปลงระหว่างประเภท / โปรแกรมและโลกแห่งทฤษฎีบท / การพิสูจน์นั้นเป็นแบบกลไกและในหลาย ๆ กรณีอาจเป็นไปโดยอัตโนมัติ

Curry-Howard มีบทบาทอย่างดีในสิ่งที่เราต้องการทำกับข้อกำหนดสำหรับโปรแกรม

ระบบประเภทมีประโยชน์ใน Java หรือไม่?

แม้จะมีความเข้าใจใน Curry-Howard แต่บางคนก็พบว่ามันง่ายที่จะละทิ้งคุณค่าของระบบประเภทเมื่อเป็นเช่นนั้น

  1. เป็นเรื่องยากมากที่จะทำงานด้วย
  2. สอดคล้อง (ผ่าน Curry-Howard) กับตรรกะที่มีการแสดงออกที่ จำกัด
  3. เสีย (ซึ่งได้รับการกำหนดลักษณะของระบบว่า "อ่อนแอ" หรือ "แข็งแกร่ง")

เกี่ยวกับประเด็นแรกบางที IDE อาจทำให้ระบบประเภทของ Java ง่ายพอที่จะทำงานร่วมกับ (ซึ่งเป็นอัตนัยสูง)

เกี่ยวกับประเด็นที่สอง Java เกิดขึ้นเกือบจะสอดคล้องกับตรรกะลำดับที่หนึ่ง Generics ให้ใช้ระบบประเภทที่เทียบเท่ากับการหาปริมาณสากล น่าเสียดายที่ไวด์การ์ดให้การหาปริมาณอัตถิภาวนิยมเพียงเล็กน้อยเท่านั้น แต่การหาปริมาณแบบสากลเป็นการเริ่มต้นที่ดี เป็นเรื่องดีที่สามารถพูดได้ว่าฟังก์ชันสำหรับการList<A>ทำงานในระดับสากลสำหรับรายการที่เป็นไปได้ทั้งหมดเนื่องจาก A ไม่มีข้อ จำกัด อย่างสมบูรณ์ สิ่งนี้นำไปสู่สิ่งที่ผู้ใช้ Twitter กำลังพูดถึงเกี่ยวกับ "พาราเมตริก"

บทความที่มักอ้างถึงเกี่ยวกับพาราเมตริกคือTheoremsของ Philip Wadler ฟรี! . สิ่งที่น่าสนใจเกี่ยวกับบทความนี้คือจากลายเซ็นประเภทเพียงอย่างเดียวเราสามารถพิสูจน์ค่าคงที่ที่น่าสนใจได้ ถ้าเราจะเขียนการทดสอบอัตโนมัติสำหรับค่าคงที่เหล่านี้เราจะเสียเวลาอย่างมาก ตัวอย่างเช่นList<A>จากลายเซ็นชนิดเพียงอย่างเดียวสำหรับflatten

<A> List<A> flatten(List<List<A>> nestedLists);

เราสามารถให้เหตุผลว่า

flatten(nestedList.map(l -> l.map(any_function)))
    ≡ flatten(nestList).map(any_function)

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

การไม่ลบอาจนำไปสู่การละเมิดได้

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

  • ผลข้างเคียงกับระบบภายนอก
  • การสะท้อนกลับ

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

แล้วยาชื่อสามัญที่ไม่ถูกลบมีวิธีใดบ้างที่ "มีประโยชน์" ลองพิจารณาการใช้งานที่กล่าวถึงในทวีต:

<T> T broken { return new T(); }

จะเกิดอะไรขึ้นถ้า T ไม่มีตัวสร้าง no-arg? ในบางภาษาสิ่งที่คุณได้รับเป็นค่าว่าง หรือบางทีคุณอาจข้ามค่า null และไปที่การเพิ่มข้อยกเว้น (ซึ่งค่า null ดูเหมือนจะนำไปสู่อยู่ดี) เนื่องจากภาษาของเราเป็นภาษาทัวริงสมบูรณ์จึงเป็นไปไม่ได้ที่จะให้เหตุผลว่าคำเรียกbrokenใดจะเกี่ยวข้องกับประเภทที่ "ปลอดภัย" โดยไม่มีตัวสร้างที่ไม่มีข้อโต้แย้งและประเภทใดจะไม่ใช้ เราสูญเสียความมั่นใจว่าโปรแกรมของเราทำงานได้ในระดับสากล

การลบหมายความว่าเราได้ให้เหตุผลแล้ว (มาลบกันเถอะ)

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

การลบกระตุ้นการใช้เหตุผล


7
ในช่วงเวลาที่ฉันใช้คำตอบนี้ร่วมกันบางคนก็ตอบด้วยคำตอบที่คล้ายกัน แต่เมื่อเขียนมากขนาดนี้ฉันตัดสินใจที่จะโพสต์แทนที่จะทิ้งมัน
Sukant Hajra

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

5
พื้นฐานด้วยความเคารพคุณไม่ได้โต้แย้งกัน คุณเพียงยืนยันว่าการลบเป็น "การทำให้หมดประโยชน์" โดยไม่คำนึงถึงคุณค่าของการพิสูจน์เวลาคอมไพล์ในฐานะเครื่องมือที่น่าสนใจสำหรับการให้เหตุผลเกี่ยวกับโปรแกรม นอกจากนี้ประโยชน์เหล่านี้ยังมีมุมฉากอย่างสมบูรณ์ของเส้นทางที่คดเคี้ยวและแรงจูงใจที่ Java นำมาใช้ในการปรับใช้ยาสามัญ นอกจากนี้ผลประโยชน์เหล่านี้ยังมีไว้สำหรับภาษาที่มีการใช้การลบที่รู้แจ้งมากกว่า
Sukant Hajra

46
ข้อโต้แย้งที่นำเสนอในที่นี้ไม่ได้ต่อต้านการลบ แต่เป็นการต่อต้านการสะท้อน (และการตรวจสอบประเภทรันไทม์โดยทั่วไป) - โดยพื้นฐานแล้วยืนยันว่าการสะท้อนนั้นไม่ดีดังนั้นความจริงที่ว่าการลบไม่ได้ผลดีกับสิ่งเหล่านี้จึงเป็นสิ่งที่ดี เป็นจุดที่ถูกต้องในตัวเอง (แม้ว่าหลายคนจะไม่เห็นด้วยก็ตาม); แต่มันไม่ค่อยสมเหตุสมผลในบริบทเนื่องจาก Java มีการตรวจสอบการสะท้อน / รันไทม์อยู่แล้วและพวกเขาไม่ได้และจะไม่หายไป ดังนั้นการมีโครงสร้างที่ไม่เล่นกับส่วนที่มีอยู่และไม่ได้เลิกใช้งานของภาษานั้นเป็นข้อ จำกัด ไม่ว่าคุณจะมองอย่างไร
Pavel Minaev

10
คำตอบของคุณไม่ตรงประเด็น คุณป้อนคำอธิบายที่ยืดยาว (แม้ว่าจะน่าสนใจในแง่ของมันเอง) ว่าเหตุใดการลบประเภทเป็นแนวคิดนามธรรมโดยทั่วไปจึงมีประโยชน์ในการออกแบบระบบประเภท แต่หัวข้อนี้เป็นประโยชน์ของลักษณะเฉพาะของ Java Generics ที่มีข้อความ "การลบประเภท "ซึ่งคล้ายกับแนวคิดที่คุณอธิบายเพียงผิวเผิน แต่ล้มเหลวอย่างสิ้นเชิงในการให้ค่าคงที่ที่น่าสนใจและการแนะนำข้อ จำกัด ที่ไม่สมเหตุสมผล
Marko Topolnik

41

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

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

Java แบ่งการพิมพ์แบบคงที่โดยอนุญาตให้สืบค้นข้อมูลประเภทที่รันไทม์ - การสะท้อนกลับ, อินสแตนซ์ของ ฯลฯ สิ่งนี้ช่วยให้คุณสร้างโปรแกรมที่ไม่สามารถตรวจสอบแบบคงที่ได้ซึ่งจะข้ามระบบประเภท นอกจากนี้ยังพลาดโอกาสในการเพิ่มประสิทธิภาพแบบคงที่

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

การลบเป็นสิ่งสำคัญสำหรับการรักษาคุณสมบัติของ "พาราเมตริก" ของชนิดข้อมูล สมมติว่าฉันมีพารามิเตอร์ประเภท "รายการ" เหนือส่วนประกอบประเภท T. เช่นรายการ <T> ประเภทนั้นเป็นประพจน์ที่ประเภทรายการนี้ทำงานเหมือนกันสำหรับประเภทใด ๆ T. ข้อเท็จจริงที่ว่า T เป็นพารามิเตอร์ประเภทนามธรรมที่ไม่มีขอบเขตหมายความว่าเราไม่รู้อะไรเกี่ยวกับประเภทนี้ดังนั้นจึงถูกป้องกันไม่ให้ทำอะไรเป็นพิเศษสำหรับกรณีพิเศษของ T.

เช่นบอกว่าฉันมี List xs = asList ("3") ฉันเพิ่มองค์ประกอบ: xs.add ("q") ฉันลงท้ายด้วย ["3", "q"] เนื่องจากนี่คือพาราเมตริกฉันจึงสามารถสันนิษฐานได้ว่า List xs = asList (7); xs.add (8) ลงท้ายด้วย [7,8] ฉันรู้จากประเภทที่ว่ามันไม่ได้ทำสิ่งหนึ่งสำหรับ String และอีกสิ่งหนึ่งสำหรับ Int

นอกจากนี้ฉันทราบว่าฟังก์ชัน List.add ไม่สามารถสร้างค่า T จากอากาศบาง ๆ ได้ ฉันรู้ว่าถ้า asList ของฉัน ("3") มี "7" เพิ่มเข้าไปคำตอบเดียวที่เป็นไปได้จะถูกสร้างจากค่า "3" และ "7" ไม่มีความเป็นไปได้ที่จะเพิ่ม "2" หรือ "z" ลงในรายการเนื่องจากฟังก์ชันจะไม่สามารถสร้างได้ ค่าอื่น ๆ เหล่านี้ไม่สมเหตุสมผลที่จะเพิ่มและพาราเมตริกจะป้องกันไม่ให้สร้างโปรแกรมที่ไม่ถูกต้องเหล่านี้

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


15

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


เป็นที่ถกเถียงกันมากว่ากระบวนการที่ทำบน Java Generics นั้นสมควรได้รับชื่อ "type erasure" หรือไม่ เนื่องจากประเภททั่วไปไม่ได้ถูกลบออก แต่แทนที่ด้วยตัวเลือกที่ดีกว่าจึงน่าจะเป็น "ประเภทการตัด"

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

การลบประเภทของ Java ไม่บรรลุผล - มันทำให้คอมไพเลอร์พิการเช่นในตัวอย่างนี้:

void doStuff(List<Integer> collection) { 
}

void doStuff(List<String> collection) // ERROR: a method cannot have 
                   // overloads which only differ in type parameters

(การประกาศสองรายการข้างต้นยุบเป็นลายเซ็นวิธีเดียวกันหลังจากการลบ)

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

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

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

( extends Objectต้องเพิ่มความซ้ำซ้อนเพื่อรักษาความเข้ากันได้แบบย้อนหลังของลายเซ็นที่ถูกลบ)

ตอนนี้ให้เรากลับมาทบทวนคำพูด:

เป็นเรื่องตลกเมื่อผู้ใช้ Java บ่นเกี่ยวกับการลบประเภทซึ่งเป็นสิ่งเดียวที่ Java มีสิทธิ์

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


10

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

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

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



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

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


ขอบคุณสำหรับสิ่งนั้น เป็นความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับปรัชญาที่เกี่ยวข้องซึ่งฉันคาดว่าจะได้เห็นสำหรับคำถามนี้
Daniel Langdon

การจัดส่งแบบไดนามิกไม่มีการพึ่งพาใด ๆ ในประเภท reified มักอาศัยสิ่งที่วรรณกรรมการเขียนโปรแกรมเรียกว่า "แท็ก" แต่ถึงอย่างนั้นก็ไม่จำเป็นต้องใช้แท็ก หากคุณสร้างอินเทอร์เฟซ (สำหรับอ็อบเจ็กต์) และสร้างอินสแตนซ์แบบไม่ระบุตัวตนของอินเทอร์เฟซนั้นแสดงว่าคุณกำลังทำการจัดส่งแบบไดนามิกโดยไม่จำเป็นต้องใช้ประเภท reified เลย
Sukant Hajra

@sukanthajra นั่นเป็นเพียงการแยกผม แท็กคือข้อมูลรันไทม์ที่คุณต้องการเพื่อแก้ไขลักษณะการทำงานที่อินสแตนซ์จัดแสดง มันอยู่ไกลจากการเข้าถึงของระบบประเภทคงที่และนั่นคือสาระสำคัญของแนวคิดภายใต้การสนทนา
Marko Topolnik

มีประเภทคงที่ซึ่งออกแบบมาสำหรับเหตุผลนี้อย่างแท้จริงเรียกว่า "ประเภทผลรวม" เรียกว่า "ประเภทตัวแปร" ด้วย (ในหนังสือประเภทและภาษาโปรแกรมที่ดีมากของเบนจามินเพียร์ซ) นี่ไม่ใช่การแยกผมเลย นอกจากนี้คุณยังสามารถใช้ catamorphism / fold / visitor สำหรับวิธีการที่ไม่มีแท็กอย่างสมบูรณ์
Sukant Hajra

ขอบคุณสำหรับบทเรียนทฤษฎี แต่ฉันไม่เห็นความเกี่ยวข้อง หัวข้อของเราคือระบบประเภทของ Java
Marko Topolnik

9

โพสต์ต่อมาโดยผู้ใช้คนเดียวกันในการสนทนาเดียวกัน:

new T เป็นโปรแกรมที่เสีย มันเป็นไอโซมอร์ฟิกสำหรับคำกล่าวอ้าง "ข้อเสนอทั้งหมดเป็นจริง" ฉันไม่ใหญ่ในเรื่องนี้

(นี่เป็นการตอบสนองต่อคำสั่งของผู้ใช้รายอื่นกล่าวคือ "ดูเหมือนว่าในบางสถานการณ์" T ใหม่ "จะดีกว่า" ความคิดที่new T()เป็นไปไม่ได้เนื่องจากการลบประเภท (สิ่งนี้เป็นที่ถกเถียงกัน - แม้ว่าจะTมีอยู่ที่ รันไทม์อาจเป็นคลาสนามธรรมหรืออินเทอร์เฟซหรืออาจเป็นได้Voidหรืออาจไม่มีตัวสร้างที่ไม่มีอาร์กิวเมนต์หรือตัวสร้าง no-arg อาจเป็นแบบส่วนตัว (เช่นเพราะมันควรจะเป็นคลาสซิงเกิลตัน) หรือ ตัวสร้าง no-arg สามารถระบุข้อยกเว้นที่ตรวจสอบแล้วซึ่งวิธีการทั่วไปไม่สามารถจับหรือระบุได้ - แต่นั่นเป็นหลักฐานไม่ว่าจะเป็นความจริงที่ว่าอย่างน้อยคุณก็สามารถเขียนได้โดยไม่ต้องลบT.class.newInstance()ซึ่งจัดการปัญหาเหล่านั้นได้))

มุมมองนี้ว่าประเภทเป็นไอโซมอร์ฟิกกับประพจน์แสดงให้เห็นว่าผู้ใช้มีพื้นฐานในทฤษฎีประเภทที่เป็นทางการ (S) เขามักจะไม่ชอบ "ประเภทไดนามิก" หรือ "ประเภทรันไทม์" และต้องการ Java ที่ไม่มีการดาวน์แคสต์instanceofและการสะท้อนและอื่น ๆ (ลองนึกถึงภาษาอย่าง Standard ML ซึ่งมีระบบประเภทที่สมบูรณ์มาก (คงที่) และความหมายแบบไดนามิกไม่ได้ขึ้นอยู่กับข้อมูลประเภทใด ๆ )

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


2
ไม่ได้รวบรวมใน Java เนื่องจากมีการลบประเภท
Mark Rotteveel

1
@MarkRotteveel: ขอบคุณ; ฉันได้เพิ่มย่อหน้าเพื่ออธิบาย (และแสดงความคิดเห็น) นั้น
ruakh

1
เมื่อพิจารณาจากส่วนผสมของการโหวตเพิ่มและการโหวตลดลงนี่เป็นคำตอบที่ถกเถียงกันมากที่สุดที่ฉันเคยโพสต์ ฉันภูมิใจอย่างประหลาด
ruakh

6

สิ่งที่ดีอย่างหนึ่งคือไม่จำเป็นต้องเปลี่ยน JVM เมื่อมีการเปิดตัวยาชื่อสามัญ Java ใช้ generics ในระดับคอมไพเลอร์เท่านั้น


1
การเปลี่ยนแปลงของ JVM เทียบกับอะไร? เทมเพลตไม่จำเป็นต้องมีการเปลี่ยนแปลง JVM เช่นกัน
Marquis of Lorne

5

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

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

public List<Integer> XXX(final List<Integer> l);

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

public <T> List<T> XXX(final List<T> l);

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

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

ถ้า Java ไม่ได้มีการลบออกประเภท (นั่นคือข้อโต้แย้งประเภทถูก reified ที่รันไทม์) แล้วนี้ก็จะช่วยให้มากขึ้นแง่เหตุผล-impairing ชนิดนี้ ในตัวอย่างข้างต้นการนำไปใช้สามารถละเมิดความคาดหวังที่กำหนดโดยลายเซ็นประเภทเท่านั้นหากรายการมีอย่างน้อยหนึ่งองค์ประกอบ แต่ถ้าTถูก reified ก็สามารถทำได้แม้ว่ารายการจะว่างเปล่าก็ตาม ประเภท Reified จะเพิ่มความเป็นไปได้ (มากแล้ว) ในการขัดขวางความเข้าใจของเราเกี่ยวกับรหัส

การลบประเภททำให้ภาษา "มีประสิทธิภาพ" น้อยลง แต่ "อำนาจ" บางรูปแบบนั้นเป็นอันตรายจริง


1
ดูเหมือนว่าคุณกำลังโต้เถียงว่าชื่อสามัญนั้นซับซ้อนเกินไปสำหรับนักพัฒนา Java ที่จะ "เข้าใจและให้เหตุผล" หรือว่าพวกเขาไม่สามารถไว้วางใจได้ว่าจะไม่ยิงตัวตายหากได้รับโอกาส หลังดูเหมือนจะซิงค์กับ ethos ของ Java (เช่นเดียวกับประเภทที่ไม่ได้ลงนาม ฯลฯ ) แต่ดูเหมือนว่านักพัฒนาจะเอื้ออำนวยเล็กน้อย
Basic

1
@ พื้นฐานฉันต้องแสดงตัวเองได้แย่มากเพราะนั่นไม่ใช่สิ่งที่ฉันหมายถึงเลย ปัญหาไม่ได้อยู่ที่ชื่อสามัญมีความซับซ้อน แต่สิ่งต่างๆเช่นการคัดเลือกนักแสดงและinstanceofขัดขวางความสามารถของเราในการให้เหตุผลเกี่ยวกับสิ่งที่รหัสทำตามประเภท ถ้า Java ต้อง reify อาร์กิวเมนต์ประเภทมันจะทำให้ปัญหานี้แย่ลง การลบประเภทในรันไทม์มีผลทำให้ระบบ type มีประโยชน์มากขึ้น
Lachlan

@lachlan มันสมเหตุสมผลสำหรับฉันและฉันไม่คิดว่าการอ่านแบบปกติจะทำให้เกิดการดูถูกต่อนักพัฒนา Java
Rob Grant

3

นี่ไม่ใช่คำตอบโดยตรง (OP ถามว่า "ประโยชน์คืออะไร" ฉันกำลังตอบว่า "ข้อเสียคืออะไร")

เมื่อเทียบกับระบบประเภท C # การลบประเภท Java เป็นความเจ็บปวดที่แท้จริงสำหรับสอง raesons

คุณไม่สามารถใช้งานอินเทอร์เฟซได้สองครั้ง

ใน C # คุณสามารถใช้ทั้งสองอย่างIEnumerable<T1>และIEnumerable<T2>ปลอดภัยโดยเฉพาะอย่างยิ่งถ้าทั้งสองประเภทไม่มีบรรพบุรุษร่วมกัน (เช่นบรรพบุรุษของพวกเขาคือ Object )

ตัวอย่างที่ใช้ได้จริง: ใน Spring Framework คุณไม่สามารถใช้งานได้ApplicationListener<? extends ApplicationEvent>หลายครั้ง หากคุณต้องการพฤติกรรมที่แตกต่างกันตามที่Tคุณต้องการทดสอบinstanceof

คุณไม่สามารถทำ T () ใหม่ได้

(และคุณต้องมีการอ้างอิงถึงคลาสเพื่อทำเช่นนั้น)

ตามที่คนอื่น ๆ แสดงความคิดเห็นการทำสิ่งที่เทียบเท่าnew T()สามารถทำได้โดยการสะท้อนเท่านั้นโดยการเรียกใช้อินสแตนซ์Class<T>ตรวจสอบให้แน่ใจเกี่ยวกับพารามิเตอร์ที่ตัวสร้างต้องการ C # ช่วยให้คุณทำได้new T() ก็ต่อเมื่อคุณ จำกัด ตัวTสร้างแบบไม่มีพารามิเตอร์ หากTไม่เคารพข้อ จำกัด ดังกล่าวข้อผิดพลาดในการคอมไพล์จะเพิ่มขึ้น

ใน Java คุณมักจะถูกบังคับให้เขียนเมธอดที่มีลักษณะดังต่อไปนี้

public <T> T create(....params, Class<T> classOfT)
{

    ... whatever you do
    ... you will end up
    T = classOfT.newInstance();


    ... or more advanced reflection
    Constructor<T> parameterizedConstructorThatYouKnowAbout = classOfT.getConstructor(...,...);
}

ข้อเสียในโค้ดด้านบนคือ:

  • Class.newInstanceใช้งานได้กับคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์เท่านั้น หากไม่มีReflectiveOperationExceptionจะถูกโยนไปที่รันไทม์
  • ตัวสร้างแบบสะท้อนไม่ได้เน้นปัญหาในเวลาคอมไพล์เหมือนข้างบน หากคุณ refactor คุณสลับอาร์กิวเมนต์คุณจะรู้เฉพาะในรันไทม์เท่านั้น

หากฉันเป็นผู้เขียน C # ฉันจะแนะนำความสามารถในการระบุข้อ จำกัด ของตัวสร้างหนึ่งข้อหรือมากกว่าที่ตรวจสอบได้ง่ายในเวลาคอมไพล์ (ดังนั้นฉันสามารถต้องการตัวอย่างเช่นตัวสร้างที่มีstring,stringพารามิเตอร์) แต่สุดท้ายคือการเก็งกำไร


2

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

public class GenericClass<T>
{
     private Class<T> targetClass;
     public GenericClass(Class<T> targetClass)
     {
          this.targetClass = targetClass;
     }

จากนั้นคลาสนี้จะสามารถทำทุกสิ่งที่จะทำได้ตามค่าเริ่มต้นหาก Java ไม่ได้ใช้การลบ: มันสามารถจัดสรรTs ใหม่(สมมติว่าTมีตัวสร้างที่ตรงกับรูปแบบที่คาดว่าจะใช้) หรืออาร์เรย์ของTs ก็สามารถทำได้ ทดสอบแบบไดนามิก ณ รันไทม์ว่าออบเจ็กต์หนึ่งเป็น a Tและเปลี่ยนพฤติกรรมขึ้นอยู่กับสิ่งนั้นและอื่น ๆ

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

     public T newT () { 
         try {
             return targetClass.newInstance(); 
         } catch(/* I forget which exceptions can be thrown here */) { ... }
     }

     private T value;
     /** @throws ClassCastException if object is not a T */
     public void setValueFromObject (Object object) {
         value = targetClass.cast(object);
     }
}

ใครสนใจที่จะอธิบายเหตุผลของการโหวตลง? เท่าที่ฉันเห็นนี่เป็นคำอธิบายที่ดีอย่างสมบูรณ์ว่าทำไมข้อเสียของระบบการลบประเภทของ Java จึงไม่ได้เป็นข้อ จำกัด ที่แท้จริงเลย แล้วปัญหาคืออะไร?
Jules

ฉันกำลังโหวต ไม่ใช่ว่าฉันสนับสนุนการตรวจสอบประเภทรันไทม์ แต่เคล็ดลับนี้มีประโยชน์ในเหมืองเกลือ
techtangents

3
Java เป็นภาษาของเหมืองเกลือซึ่งทำให้สิ่งนี้เป็นสิ่งจำเป็นเนื่องจากไม่มีข้อมูลประเภทรันไทม์ หวังว่าประเภทการลบจะถูกยกเลิกใน Java เวอร์ชันอนาคตทำให้ generics เป็นคุณสมบัติที่มีประโยชน์มากขึ้น ขณะนี้ฉันทามติคืออัตราส่วนแรงขับ / น้ำหนักต่ำกว่า 1
Marko Topolnik

แน่นอน ... ตราบใดที่ TargetType ของคุณไม่ได้ใช้ generics เอง แต่ดูเหมือนจะ จำกัด
พื้นฐาน

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

0

หลีกเลี่ยง c ++ - เช่น code bloat เนื่องจากใช้รหัสเดียวกันสำหรับหลายประเภท อย่างไรก็ตามการลบประเภทต้องใช้การจัดส่งเสมือนในขณะที่ c ++ - วิธีการขยายรหัสสามารถทำการทั่วไปที่ไม่ได้จัดส่ง


3
แม้ว่าการจัดส่งเสมือนส่วนใหญ่เหล่านี้จะถูกแปลงเป็นแบบคงที่ในรันไทม์ดังนั้นจึงไม่เลวร้ายอย่างที่คิด
Piotr Kołaczkowski

1
จริงมากความพร้อมใช้งานของคอมไพเลอร์ในขณะรันไทม์ช่วยให้ java ทำสิ่งดีๆมากมาย (และบรรลุประสิทธิภาพสูง) เมื่อเทียบกับ c ++
necromancer

ว้าวให้ฉันเขียนมันลงไปที่ไหนสักแห่งที่java มีประสิทธิภาพสูงเมื่อเทียบกับ cpp ..
jungle_mole

1
@jungle_mole ใช่ขอบคุณ มีเพียงไม่กี่คนที่ตระหนักถึงประโยชน์ของการมีคอมไพเลอร์พร้อมใช้งานเพื่อรวบรวมโค้ดอีกครั้งหลังจากทำโปรไฟล์ (หรือว่าการเก็บขยะเป็นเรื่องใหญ่ (ไม่ใช่ขยะ) แทนที่จะเป็นความเชื่อที่ไร้เดียงสาและไม่ถูกต้องว่าการเก็บขยะเป็นเรื่องใหญ่ (ขยะ) (หรือในขณะที่การเก็บขยะเป็นความพยายามเพียงเล็กน้อย แต่ก็ชดเชยด้วยการยอมให้ต้นทุนเป็นศูนย์ การจัดสรร)). เป็นโลกที่น่าเศร้าที่ผู้คนยอมรับสิ่งที่ชุมชนของตนเชื่ออย่างสุ่มสี่สุ่มห้าและเลิกใช้เหตุผลจากหลักการแรก
necromancer

0

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

และแม้ว่าคำถามนี้จะมีอายุมากกว่า 5 ปี แต่คำถามก็ยังคงอยู่: เหตุใดการลบประเภทจึงเป็นที่ต้องการจากมุมมองทางเทคนิค ท้ายที่สุดคำตอบก็ค่อนข้างง่าย (ในระดับที่สูงขึ้น): https://en.wikipedia.org/wiki/Type_erasure

ไม่มีเทมเพลต C ++ ในรันไทม์ คอมไพเลอร์จะปล่อยเวอร์ชันที่ปรับให้เหมาะสมเต็มที่สำหรับการเรียกแต่ละครั้งซึ่งหมายความว่าการดำเนินการไม่ได้ขึ้นอยู่กับข้อมูลประเภท แต่ JIT จัดการกับฟังก์ชันเดียวกันในเวอร์ชันต่างๆอย่างไร? แค่มีฟังก์ชันเดียวจะดีกว่าไหม? ไม่ต้องการให้ JIT ต้องปรับให้เหมาะสมกับเวอร์ชันต่างๆทั้งหมด แต่แล้วความปลอดภัยประเภทล่ะ? เดาว่าต้องออกไปนอกหน้าต่าง

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

วิธีการเขียนโปรแกรมทั่วไปด้วยการลบประเภทนี้จะอยู่ใกล้กับค่าใช้จ่ายเป็นศูนย์ (ยังคงต้องมีการตรวจสอบรันไทม์ / แคสต์บางอย่าง): https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

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