ฉันได้ตรวจสอบรหัสสำหรับเพื่อนร่วมงานเมื่อสองวันก่อน ฉันรู้สึกว่าฉันไม่ได้มีความเข้าใจอย่างถ่องแท้เกี่ยวกับ Java Generics ดังนั้นฉันจึงหยิบหนังสือ "Effective Java" 1 แล้วดูบทที่เกี่ยวข้อง ในรายการที่ 24: กำจัดส่วนคำเตือนที่ไม่ได้ตรวจสอบผู้เขียนใช้วิธีการสาธารณะ <t> t [] toarray (t [] a) ในคลาส ArrayList เป็นตัวอย่างเพื่อแสดงวิธีการใช้ @SuppressWarnings คำอธิบายประกอบสำหรับตัวแปร
ArrayList เป็นคลาสทั่วไปซึ่งประกาศเช่นนี้:
Javapublic Class Arraylist <E> ขยาย AbstractList <E> ใช้รายการ <E>, RandomAccess, cloneable, java.io.serializable
วิธี Toarray (t [] a) ของคลาสนี้เป็นวิธีทั่วไปซึ่งประกาศและนำไปใช้เช่นนี้:
@suppresswarnings ("ไม่ได้ตรวจสอบ") สาธารณะ <t> t [] toarray (t [] a) {ถ้า (A.Length <size) // สร้างอาร์เรย์ใหม่ของประเภทรันไทม์ของ A แต่เนื้อหาของฉัน: return (t []) array.copyof (ElementData, Size, A. ขนาด) a [size] = null; return a;} วิธีนี้ถูกประกาศจริงในอินเทอร์เฟซคอลเลกชัน เนื่องจากเรามักจะใช้มันผ่าน ArrayList เราจะใช้ ArrayList เป็นตัวอย่างที่นี่
1 ทำไมจึงประกาศว่าเป็นประเภทอื่น?
คำถามของฉันคือ: ทำไมวิธีนี้ใช้ Type T แทน Type E ของ ArrayList? นั่นคือทำไมวิธีนี้จึงไม่ได้ประกาศเช่นนี้:
javapublic e [] toarray (e [] a);
หากประเภทเหมือนกันข้อผิดพลาดประเภทพารามิเตอร์สามารถพบได้ในระหว่างการรวบรวม หากประเภทต่างกันมันเป็นเรื่องง่ายที่จะสร้างข้อผิดพลาดรันไทม์ ตัวอย่างเช่นรหัสต่อไปนี้:
// สร้าง arraylistlist <String> strlist = new ArrayList <String> (); strlist.add ("abc"); strlist.add ("xyz"); // แปลง strlist ปัจจุบันเป็นอาร์เรย์ตัวเลข โปรดทราบว่าไม่มีข้อผิดพลาดในการรวบรวมในคำสั่งต่อไปนี้ number [] numarray = strlist.toarray (หมายเลขใหม่ [0]); เรียกใช้รหัสด้านบนและบรรทัดที่ 6 จะโยน java.lang.arraystoreexception ยกเว้น
หากวิธี TOARRAY ใช้ Type E คำสั่ง 2 จะสร้างข้อผิดพลาดในการรวบรวม ข้อผิดพลาดในการรวบรวมเป็นมิตรมากกว่าข้อผิดพลาดรันไทม์ ยิ่งไปกว่านั้นวัตถุประสงค์หลักของทั่วไปคือการกำจัดข้อผิดพลาดการแปลงประเภท (ClassCastException) ในระหว่างการรวบรวม วิธีนี้ตรงกันข้าม นี่เป็นข้อผิดพลาดใหญ่หรือไม่? ฉันพบข้อผิดพลาด Java แต่ฉันก็ยังไม่อยากเชื่อเลย
หลังจากตรวจสอบอินเทอร์เน็ตปัญหานี้ได้รับการหารือกันหลายครั้ง 2, 3, 4
2 สามารถปรับปรุงความยืดหยุ่น
การประกาศนี้มีความยืดหยุ่นมากขึ้นและสามารถแปลงองค์ประกอบในรายการปัจจุบันเป็นอาร์เรย์ประเภททั่วไปเพิ่มเติม ตัวอย่างเช่นประเภทรายการปัจจุบันเป็นจำนวนเต็มและเราสามารถแปลงองค์ประกอบเป็นอาร์เรย์ตัวเลข
รายการ <จำนวนเต็ม> intlist = new ArrayList <integer> (); intlist.add (1); intlist.add (2); number [] numarray = intlist.toarray (หมายเลขใหม่ [0]);
หากวิธีการนี้ถูกประกาศว่าเป็นประเภท E รหัสด้านบนจะมีข้อผิดพลาดในการรวบรวม ดูเหมือนว่ามันจะเหมาะสมกว่าที่จะประกาศวิธีการดังนี้:
Javapublic <t super e> t [] toarray (t [] a);
อย่างไรก็ตามไวยากรณ์เช่น <t super e> ไม่มีอยู่ใน Java และแม้ว่ามันจะมีอยู่มันก็ไม่ได้ผลสำหรับอาร์เรย์ ด้วยเหตุนี้เมื่อใช้วิธีนี้แม้ว่า t คือคลาสแม่ของ E หรือ t นั้นเหมือนกับ e, java.lang.arraystoreexception 5, 6, 7 ไม่สามารถหลีกเลี่ยงได้อย่างสมบูรณ์ โปรดดูสองรหัสต่อไปนี้ ในรหัสแรก T คือคลาสหลักของ E และในรหัสที่สอง t เหมือนกับ E ทั้งสองชิ้นของการโยนรหัส
รหัส 1:
รายการ <จำนวนเต็ม> intlist = new ArrayList <Integer> (); intlist.add (1); intlist.add (2); float [] floatarray = float ใหม่ [2]; // float เป็น subclass ของจำนวนดังนั้น float [] เป็น subclass ของหมายเลข [] หมายเลข [] numarray = floatarray; // คำสั่งต่อไปนี้
รหัส 2:
รายการ <gumber> intlist = new ArrayList <Number> (); // ประเภทของรายการคือหมายเลข แต่ตัวเลขเป็นคลาสนามธรรมและสามารถจัดเก็บอินสแตนซ์ของ subclass intlist.add (ใหม่จำนวนเต็ม ()); intlist.add (New Integer ()); float [] floatarray = new float []; // float เป็น subclass ของจำนวนดังนั้น float [] เป็น subclass ของหมายเลข [] หมายเลข [] numarray = floatarray; // คำสั่งต่อไปนี้
ข้อยกเว้นข้างต้นทั้งหมดเกิดจากข้อเท็จจริงนี้: หาก A คือคลาสหลักของ B ดังนั้น A [] คือคลาสหลักของ B [] คลาสทั้งหมดใน Java ที่สืบทอดมาจากวัตถุและ Object [] เป็นคลาสหลักของอาร์เรย์ทั้งหมด
โพสต์นี้ 8 ให้ตัวอย่างแสดงว่าแม้ว่าประเภทของวิธีนี้จะถูกประกาศเป็น E แต่ก็ไม่สามารถหลีกเลี่ยงได้
ข้อยกเว้นนี้ยังกล่าวถึงในเอกสารสำหรับวิธีนี้:
arraystoreexception หากประเภทรันไทม์ของอาร์เรย์ที่ระบุไม่ใช่ supertype ของประเภทรันไทม์ของทุกองค์ประกอบในรายการนี้
3 สามารถเข้ากันได้กับรุ่นก่อน Java 1.5
วิธีนี้ปรากฏขึ้นก่อนที่จะมีการเปิดตัวยาชื่อสามัญใน Java (มีการแนะนำทั่วไปใน JDK1.5) มีการประกาศในเวลานั้น:
Javapublic Object [] ToArray (Object [] A)
หลังจากปรากฏตัวทั่วไปแล้วคลาสและวิธีการหลายอย่างจะกลายเป็นทั่วไป วิธีนี้ยังประกาศเช่นนี้:
javapublic <t> t [] toarray (t [] a)
การประกาศนี้เข้ากันได้กับรุ่นก่อน Java 1.5
4 คำอีกสองสามคำ
วิธีนี้ต้องใช้พารามิเตอร์อาร์เรย์ หากความยาวของอาร์เรย์นี้มากกว่าหรือเท่ากับขนาดของรายการปัจจุบันองค์ประกอบในรายการจะถูกเก็บไว้ในอาร์เรย์ หากความยาวของอาร์เรย์นี้น้อยกว่าขนาดของรายการปัจจุบันอาร์เรย์ใหม่จะถูกสร้างขึ้นและองค์ประกอบในรายการปัจจุบันจะถูกเก็บไว้ในอาร์เรย์ที่สร้างขึ้นใหม่ เพื่อปรับปรุงประสิทธิภาพหากเป็นไปได้ความยาวของอาร์เรย์ที่ผ่านจะต้องมากกว่าหรือเท่ากับขนาดของรายการเพื่อหลีกเลี่ยงการสร้างอาร์เรย์ใหม่ด้วยวิธีนี้
รายการ <จำนวนเต็ม> intlist = new ArrayList <integer> (); intlist.add (); intlist.add (); // วางในอาร์เรย์ความยาวของมันคือจำนวน [] numarray = intlist.toarray (หมายเลขใหม่ []); // คำสั่ง // วางในอาร์เรย์ความยาวเท่ากับความยาวของหมายเลข intlist [] numarray = intlist.toarray (หมายเลขใหม่ [intlist.size ()]); //คำแถลง
นอกจากนี้อาร์เรย์เป็นพารามิเตอร์ไม่สามารถเป็นโมฆะมิฉะนั้นจะมีการโยน nullpointerexception
เชิงอรรถ:
1
Java ที่มีประสิทธิภาพ (ฉบับที่ 2)
2
การเชื่อมโยง
3
การเชื่อมโยง
4
การเชื่อมโยง
5
การเชื่อมโยง
6
การเชื่อมโยง
7
การเชื่อมโยง
8
การเชื่อมโยง
9
การเชื่อมโยง
10
การเชื่อมโยง
สร้างขึ้น: 2016-04-06 พุธ 21:14
emacs 24.5.1 (โหมด org 8.2.10)
ตรวจสอบความถูกต้อง
เนื้อหาข้างต้นเป็นเหตุผลว่าทำไมประเภทพารามิเตอร์ของ Java Arraylist.toArray (T []) วิธีการแนะนำให้คุณรู้จักโดยบรรณาธิการคือ T แทน E ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน!