เหตุใด Java จึงไม่พิมพ์ข้อสรุป


44

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


3
กฎความเข้ากันได้ย้อนหลังเป็นภาษาที่เก่าและได้รับความนิยมอย่าง java
ratchet freak

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

1
มันอาจมีผลกระทบบางอย่างที่ไม่ได้ตั้งใจเมื่อใช้กับการลบประเภท docs.oracle.com/javase/tutorial/java/generics/ …
Zachary Yates

4
โปรดทราบว่า Java 8 จะนำการอนุมานประเภทจำนวนมากด้วยคุณสมบัติของแลมบ์ดา: คุณสามารถเขียน lambdas ที่ซับซ้อนได้โดยไม่ต้องพูดถึงประเภทใด ๆ และทุกสิ่งที่อนุมาน
Joachim Sauer

4
การอนุมานประเภทโลคัลตัวแปรกำลังมาถึง Java: JEP 286: การอนุมานประเภทตัวแปรท้องถิ่น
จิมมี่หน้า

คำตอบ:


60

เทคนิคการพูด Java มีประเภทอนุมานเมื่อใช้ generics ด้วยวิธีการทั่วไปเช่น

public <T> T foo(T t) {
  return t;
}

คอมไพเลอร์จะวิเคราะห์และเข้าใจว่าเมื่อคุณเขียน

// String
foo("bar");
// Integer
foo(new Integer(42));

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

Map<String, String> foo = new HashMap<>();

Java ใจดีพอที่จะเติมวงเล็บมุมที่ว่างสำหรับเรา ตอนนี้ทำไม Java ไม่รองรับการอนุมานประเภทเป็นส่วนหนึ่งของการกำหนดตัวแปร? จนถึงจุดหนึ่งมีRFEสำหรับการอนุมานประเภทในการประกาศตัวแปร แต่สิ่งนี้ถูกปิดเป็น "จะไม่แก้ไข" เพราะ

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

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

แน่นอนว่า RFE นั้นไม่ใช่จุดสิ้นสุดของการสนทนา ระหว่าง Java 7 คุณลักษณะนี้ถูกพิจารณาอีกครั้งโดยมีการทดสอบการใช้งานบางอย่างรวมถึง James Gosling ด้วยตัวเอง อีกครั้งคุณลักษณะนี้ถูกยิงในที่สุด

ด้วยการเปิดตัว Java 8 ตอนนี้เราได้รับการอนุมานประเภทเป็นส่วนหนึ่งของ lambdas เช่น:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

คอมไพเลอร์ Java สามารถดูวิธีการCollections#sort(List<T>, Comparator<? super T>)จากนั้นอินเทอร์เฟซของComparator#compare(T o1, T o2)และตรวจสอบว่าfirstและsecondควรจะเป็นStringดังนั้นการอนุญาตให้โปรแกรมเมอร์ที่จะไม่ต้องปรับประเภทในการแสดงออกแลมบ์ดา


5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- ใช่ถ้าเป็นเช่นHashMap<String, Integer> map = foo.getMap()นั้นฉันก็เห็นด้วย - ใน C # ฉันมักจะไม่ใช้varในกรณีเช่นนี้แม้ว่าฉันจะทำได้ HashMap<String, Integer> map = new HashMap<String, Integer>()แต่เรื่องนี้ไม่ได้ถือน้ำถ้ามัน นี่คือความซ้ำซ้อนที่แท้จริงและฉันไม่เห็นประโยชน์ในการเขียนชื่อประเภทสองครั้ง และวิธีที่ฉันจะได้รับประโยชน์จากการตรวจสอบคอมไพเลอร์ฉันไม่เข้าใจเลย
Konrad Morawski

9
ใช่และมันก็ดีสำหรับ generics แต่ฉันก็ยังคิดถึง C # varในภาษาจาวา
Konrad Morawski

15
สำหรับเรื่องนี้เป็น "un-java-like" เรื่องราว (cheesy) นี้อยู่ในใจ;) 9gag.com/gag/2308699/-this-is-how-th-things-are-done-around-here
Konrad Morawski

6
นอกจากนี้ประเภทการคืนค่าของฟังก์ชันมักจะชัดเจนหรือไม่จำเป็นและในกรณีที่ไม่ใช่ IDE ของคุณก็สามารถตั้งค่าประเภทได้ในครึ่งวินาที
Phoshi

8
ฉันเชื่อว่าใบเสนอราคาอย่างเป็นทางการHumans benefit from the redundancyคือคำตอบที่แท้จริงสำหรับคำถามปัจจุบันเพราะเหตุผลที่ฉันได้อ่านมาทั้งหมดนั้นเป็นข้อแก้ตัวที่ไร้เหตุผลและไร้สาระจากนักออกแบบ Java: ทั้ง C # และ C ++ มีคุณสมบัตินักออกแบบ C # และ C ++ Java และทุกคนมีความสุขกับการใช้งาน อะไรที่ทำให้นักพัฒนา Java แตกต่างจากนักพัฒนา C # หรือ C ++ นี่คือเหตุผลที่ฉันเห็นด้วยกับ @KonradMorawski "นี่คือสิ่งที่ทำที่นี่" ดูเหมือนจะเป็นเหตุผลเบื้องหลังจริงอีกครั้ง
paercebal

16

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

ที่กล่าวว่ามันได้รับอนุญาตสำหรับ generics เหตุผลที่มันไม่ได้รับอนุญาตสำหรับประเภทที่ไม่ใช่ทั่วไปดูเหมือนจะเป็นเพราะปรัชญา - ไม่มีอะไรป้องกันนักออกแบบจากการเพิ่มมัน

อัปเดต: ดูเหมือนว่าจาวา 10 รองรับ —- http://openjdk.java.net/jeps/286


5

เท่าที่ฉันรู้เมื่อ Java ได้รับการออกแบบในตอนต้นของการอนุมานประเภท nineties ไม่ได้รับความนิยมในหมู่ภาษากระแสหลัก (แต่มันก็เป็นแนวคิดที่รู้จักกันดีเช่นใน ML) ดังนั้นฉันสามารถจินตนาการได้ว่าการอนุมานประเภทนั้นอาจไม่ได้รับการสนับสนุนเนื่องจาก Java มุ่งเป้าไปที่โปรแกรมเมอร์ที่มาจากภาษา C ++, Pascal หรือภาษาหลักอื่น ๆ ที่ไม่ได้มี (หลักการของความประหลาดใจน้อยที่สุด)

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

ฉันไม่ทราบว่า Java จะได้รับการอนุมานประเภทในอนาคต แต่ IMO ที่จะเป็นการปฏิวัติครั้งใหญ่สำหรับภาษา (ตามที่ Glenn Nelson กล่าวถึงมันถูกอธิบายว่าเป็น "un-java-like") จากนั้นเราอาจพิจารณาทิ้ง ชื่อ Java ในความโปรดปรานของชื่อใหม่

หากคุณต้องการใช้ภาษา JVM พร้อมการอนุมานประเภทคุณสามารถใช้ Scala


2

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


8
GridBagLayout gridbag = new GridBagLayout (); เพิ่มเอกสารด้วยตนเองหรือไม่ มันซ้ำซ้อนบริสุทธิ์
Anders Lindén

3
มันทำให้เข้าใจผิดจากกรณีที่ทั้งสองประเภทไม่เหมือนกัน คุณสามารถกำหนดอินสแตนซ์ของคลาสย่อยได้อย่างง่ายดาย
jiggy

Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- ฉันไม่เข้าใจบิตนี้ การอนุมานประเภทเกิดขึ้นในขณะที่ทำการติดตั้งตัวแปรไม่เรียกใช้เมธอด คุณอาจแสดงตัวอย่างของสิ่งที่คุณหมายถึง?
Konrad Morawski

2
@ KonradMorawski: ฉันคิดว่าเขาหมายความว่าถ้าวิธีการส่งกลับ ArrayList ประเภทจะถูกอนุมานว่า หากคุณต้องการใช้ประเภทอื่นนอกเหนือจากประเภทส่งคืนคุณจะไม่สามารถใช้การอนุมานได้ แต่ฉันไม่เข้าใจว่าสิ่งนี้อาจเป็นปัญหาในโค๊ดเบสที่ใดก็ได้ที่อยู่ใกล้กับเหตุผล
Phoshi

JIT จะไม่มีส่วนร่วมในการอนุมานประเภทใด ๆ การอนุมานประเภทเป็นปรากฏการณ์เวลารวบรวม และถ้าตัวแปรถูกประกาศเป็นการอ้างอิงไปยังรายการพยายามเข้าถึงสมาชิก ArrayList ที่มันจะไม่พิมพ์ตรวจสอบและคุณได้รับข้อผิดพลาดในการคอมไพล์
sara

0

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

Collection<String> names = new ArrayList<>();

ได้อย่างมีประสิทธิภาพ

var names = new ArrayList<String>();

ไม่มีอะไรนอกจากน้ำตาล syntactic สำหรับ

ArrayList<String> names = new ArrayList<String>();

หากคุณต้องการให้ IDE ของคุณสามารถสร้างมันได้จากnew ArrayList<String>()นิพจน์ด้วย "คลิกเดียว" (refactor / สร้างตัวแปรท้องถิ่น) แต่จำไว้ว่ามันขัดแย้งกับคำแนะนำ "ใช้อินเตอร์เฟซ"

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