จุดของตัวดำเนินการไดมอนด์ (<>) ใน Java 7 คืออะไร


446

ตัวดำเนินการไดมอนด์ใน java 7 อนุญาตให้ใช้โค้ดดังนี้:

List<String> list = new LinkedList<>();

อย่างไรก็ตามใน Java 5/6 ฉันสามารถเขียน:

List<String> list = new LinkedList();

ความเข้าใจของฉันเกี่ยวกับการลบประเภทคือสิ่งเหล่านี้เหมือนกันทุกประการ (ทั่วไปจะถูกลบออกที่รันไทม์อยู่แล้ว)

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


4
โปรดทราบว่านี่ไม่ใช่ปัญหาหากคุณใช้วิธีการแบบคงที่จากโรงงานเนื่องจาก Java จะพิมพ์การอนุมานบนการเรียกเมธอด
Brian Gordon

เมื่อคุณปิดการเตือนมันไร้ประโยชน์จริง ๆ ... :) ชอบฉัน
Renetik

3
ได้รับคำตอบแล้ว แต่ยังอยู่ในบทช่วยสอน Java (ประมาณกลางหน้า): docs.oracle.com/javase/tutorial/java/generics/ ......
Andreas Tasoulas

บทความที่ดีเกี่ยวกับเรื่องนี้เมื่อวันที่DZone
R. Oosterholt

2
มุมมองของฉันมันคือน้ำตาล syntactic สำหรับ List <String> list = new LinkedList <ไม่ว่าประเภททางซ้ายคือ> (); คือการรักษามันทั่วไป
วิคเตอร์ Mellgren

คำตอบ:


497

มีปัญหากับ

List<String> list = new LinkedList();

คือว่าทางด้านซ้ายมือคุณกำลังใช้ทั่วไปประเภทList<String>ที่อยู่ทางด้านขวาคุณกำลังใช้วัตถุดิบLinkedListประเภท ประเภท Raw ใน Java นั้นมีอยู่เพียงเพื่อความเข้ากันได้กับโค้ด pre-generics และไม่ควรใช้ในรหัสใหม่เว้นแต่คุณจะต้อง

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

เท่าที่ตัวอย่างดั้งเดิมของคุณList<String> list = new LinkedList()คอมไพเลอร์สร้างคำเตือนสำหรับการมอบหมายนั้นเพราะมันจะต้อง พิจารณาสิ่งนี้:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

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

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

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

ฉันคิดว่าสิ่งสำคัญที่ต้องเข้าใจคือประเภทดิบ (ไม่มี<>) ไม่สามารถได้รับการปฏิบัติเช่นเดียวกับประเภททั่วไป เมื่อคุณประกาศประเภท raw คุณจะไม่ได้รับสิทธิประโยชน์และการตรวจสอบประเภท generics คุณต้องจำไว้ว่าgenerics เป็นจุดประสงค์ทั่วไปของภาษาจาวา ... พวกเขาไม่เพียง แต่ใช้กับผู้สร้าง no-arg ของCollections!


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

3
@Rosdi: เห็นด้วยไม่จำเป็นสำหรับประเภทดิบในรหัสใหม่ อย่างไรก็ตามฉันต้องการรวมหมายเลขเวอร์ชัน Java ไว้ในไฟล์ต้นฉบับ (แทน (mis) โดยใช้บรรทัดคำสั่ง) ดูคำตอบของฉัน
maaartinus

37
โดยส่วนตัวฉันไม่ชอบการใช้เพชรเว้นแต่คุณกำลังกำหนดและอินสแตนซ์บนบรรทัดเดียวกัน List<String> strings = new List<>()ไม่เป็นไร แต่ถ้าคุณกำหนดprivate List<String> my list;และจากนั้นลงครึ่งหนึ่งของหน้าที่คุณสร้างอินสแตนซ์ด้วยmy_list = new List<>()ก็ไม่ได้ยอดเยี่ยม! รายการของฉันมีอะไรอีก? โอ้ให้ฉันตามล่าหาคำจำกัดความ ทันใดนั้นผลประโยชน์ของทางลัดไดมอนด์นั้นก็บายไปแล้ว
rmirabelle

11
@rmirabelle นั้นแตกต่างจาก: my_list = getTheList()อย่างไร มีวิธีที่ดีกว่าหลายวิธีในการจัดการกับปัญหาประเภทนี้: 1. ใช้ IDE ที่แสดงประเภทของตัวแปรตามการเลื่อนเมาส์ 2. ใช้ชื่อตัวแปรที่มีความหมายมากกว่าเช่นprivate List<String> strings3. อย่าแยกการประกาศและการกำหนดค่าเริ่มต้นของตัวแปรเว้นแต่คุณจะต้องทำ
Natix

1
@Morfidon: ใช่มันยังคงใช้ได้สำหรับ Java 8 ฉันค่อนข้างแน่ใจว่าคุณควรได้รับคำเตือน
ColinD

37

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

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

การใช้List<String> list = new LinkedList()จะทำให้คุณได้รับคำเตือน rawtype


8
List<String> list = new LinkedList()เป็นรหัสที่ถูกต้อง คุณก็รู้เรื่องนี้และฉันก็รู้เรื่องนี้เช่นกัน และคำถาม (ตามที่ฉันเข้าใจ) คือ: เหตุใดจาวาคอมไพเลอร์เท่านั้นจึงไม่เข้าใจว่ารหัสนี้ค่อนข้างปลอดภัย
Roman

22
@Roman: List<String> list = new LinkedList()คือไม่ได้รหัสที่ถูกต้อง แน่นอนว่ามันจะดีถ้ามันเป็น! และอาจเป็นไปได้ว่าหาก Java มี generics ตั้งแต่ต้นและไม่ต้องจัดการกับความเข้ากันได้แบบย้อนหลังของประเภททั่วไปที่เคยไม่ใช่แบบทั่วไป แต่มันก็เป็นเช่นนั้น
ColinD

6
@ColinD Java ต้องการจริงๆไม่ได้ที่จะจัดการกับความเข้ากันได้ในแต่ละบรรทัดเดียว ในซอร์สไฟล์ Java ใด ๆ ที่ใช้ generics ห้ามมิให้ใช้ประเภท non-generic ประเภทเก่า (คุณสามารถใช้<?>หากเชื่อมต่อกับรหัสดั้งเดิม) และตัวดำเนินการไดมอนด์ไร้ประโยชน์ไม่ควรมีอยู่
maaartinus

16

บรรทัดนี้ทำให้คำเตือน [ไม่ถูกตรวจสอบ]:

List<String> list = new LinkedList();

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

ฉันคิดว่ามันจะเป็นงานที่ยากมากขึ้นแล้วเพิ่ม<>คุณสมบัติ

UPD : ฉันยังคิดว่าจะมีความยุ่งเหยิงถ้ามันถูกต้องตามกฎหมายที่จะใช้ประเภทดิบ 'เพียงแค่บางสิ่ง'


13

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

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

IMHO การมีวิธีที่ชัดเจนและเรียบง่ายในการทำเครื่องหมายแหล่งที่มาเป็น Java 7 จะมีประโยชน์มากกว่าการประดิษฐ์สิ่งแปลกประหลาดเช่นนั้น ในประเภทรหัสดิบที่ทำเครื่องหมายไว้อาจถูกห้ามได้โดยไม่เสียอะไรเลย

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

package 7 com.example;

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


2
คุณได้พิจารณาถึงความแตกต่างระหว่างnew ArrayList(anotherList)และnew ArrayList<>(anotherList)(โดยเฉพาะอย่างยิ่งหากได้รับมอบหมายList<String>และanotherListเป็นList<Integer>) หรือไม่?
Paul Bellora

@ พอล Bellora: ไม่น่าแปลกใจสำหรับฉันทั้งสองรวบรวม คนที่มีเพชรแม้ไม่ได้รับคำเตือน อย่างไรก็ตามฉันไม่เห็นความรู้สึกใด ๆ ในเรื่องนี้คุณช่วยอธิบายได้ไหม?
maaartinus

ขออภัยฉันอธิบายไม่ดี ดูความแตกต่างระหว่างสองตัวอย่างนี้: ideone.com/uyHaghและideone.com/ANkg3Tฉันแค่ชี้ให้เห็นว่ามันสำคัญที่จะต้องใช้ตัวดำเนินการเพชรแทนประเภทดิบอย่างน้อยเมื่อมีการส่งผ่านอาร์กิวเมนต์ที่มีขอบเขตทั่วไป ใน
พอล Bellora

ที่จริงฉันไม่ได้ใช้เวลาอ่านคำตอบของ ColinD - เขาอ้างถึงสิ่งเดียวกัน
พอล Bellora

2
new @RawType List()ดังนั้นถ้าเรากำลังจะแนะนำไวยากรณ์ใหม่สำหรับประเภทดิบสำหรับสถานที่ไม่กี่ที่มันมีความจำเป็นจริงๆทำไมไม่ใช้สิ่งที่ต้องการ นั่นเป็นที่ถูกต้องแล้ว Java 8 @RawType List = (@RawType List) genericMethod();ไวยากรณ์และประเภทคำอธิบายประกอบอนุญาตให้มีการใช้ที่ทุกสถานที่ที่จำเป็นเช่น พิจารณาว่าประเภทดิบในปัจจุบันสร้างคำเตือนคอมไพเลอร์เว้นแต่@SuppressWarningsมีการวางที่เหมาะสม@RawTypeจะเป็นการแทนที่ที่เหมาะสมและไม่จำเป็นต้องใช้ไวยากรณ์ที่ลึกซึ้งยิ่งขึ้น
Holger

8

เมื่อคุณเขียนList<String> list = new LinkedList();คอมไพเลอร์จะสร้างคำเตือน "ไม่ถูกตรวจสอบ" คุณอาจเพิกเฉย แต่ถ้าคุณไม่สนใจคำเตือนเหล่านี้คุณอาจพลาดคำเตือนที่แจ้งให้คุณทราบเกี่ยวกับปัญหาความปลอดภัยที่แท้จริง

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


4

ทั้งหมดที่กล่าวในการตอบกลับอื่น ๆ นั้นถูกต้อง แต่กรณีการใช้งานนั้นไม่สมบูรณ์ IMHO หากมีใครตรวจสอบGuava และโดยเฉพาะอย่างยิ่งสิ่งที่เกี่ยวข้องกับการสะสมสิ่งเดียวกันได้ถูกทำด้วยวิธีการคงที่ เช่นLists.newArrayList ()ซึ่งอนุญาตให้คุณเขียน

List<String> names = Lists.newArrayList();

หรือด้วยการนำเข้าคงที่

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

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

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


3

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

ข้อแตกต่างถ้าคุณระบุใน Java 5 และ 6

List<String> list = new ArrayList();

คือคุณต้องระบุ@SuppressWarnings("unchecked")ถึงlist(ไม่เช่นนั้นคุณจะได้รับคำเตือนการส่งแบบไม่ตรวจสอบ) ความเข้าใจของฉันคือผู้ประกอบการเพชรพยายามทำให้การพัฒนาง่ายขึ้น ไม่มีอะไรที่ต้องทำในการประมวลผลข้อมูลทั่วไปของรันไทม์เลย


คุณไม่จำเป็นต้องใช้คำอธิบายประกอบนั้น อย่างน้อยใน Eclipse คุณสามารถบอกคอมไพเลอร์ว่าอย่าเตือนคุณเกี่ยวกับสิ่งนี้และคุณก็สบายดี ...
Xerus

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