บทความนี้วิเคราะห์ความแตกต่างระหว่าง JDK1.4, JDK1.5 และ JDK1.6 ในการเขียนโปรแกรม Java ร่วมกับตัวอย่าง แบ่งปันสำหรับการอ้างอิงของคุณดังนี้:
พูดง่ายๆ: มีสองความแตกต่างที่ใหญ่ที่สุดระหว่าง 1.4 และ 1.5 หนึ่งคือ 1.5 มียาชื่อสามัญและอีก 1.5 สามารถห่อหุ้มประเภทข้อมูลที่ห่อหุ้มด้วยข้อมูลพื้นฐานแปดชนิดโดยอัตโนมัติ นั่นคือไม่อนุญาตให้ใช้จำนวนเต็ม A = 4 มีความแตกต่างระหว่าง 1.5 และ 1.6 ไม่มากนัก 1.6 ฉันคิดว่าการเปลี่ยนแปลงส่วนใหญ่คือ GUI ซึ่งให้การจัดการเค้าโครงและการขยายที่สะดวกมากมาย
ในช่วงเวลานี้ฉันเข้าร่วม บริษัท รัฐบาลอิเล็กทรอนิกส์และใช้ WebLogic8 จากนั้นมาใช้ JDK1.4 Eclipse เปลี่ยนเวอร์ชัน JDK อย่างไรก็ตามโครงการก่อนหน้านี้ได้กลายเป็นที่นิยม
★คุณสมบัติใหม่ของ JDK1.5:
1. ยาชื่อสามัญ
2 การบรรจุอัตโนมัติ/ยกเลิกกล่อง
3 สำหรับการสอบ
4 นำเข้าแบบคงที่
5 พารามิเตอร์ความยาวตัวแปร
1. ยาชื่อสามัญ (หลีกเลี่ยงข้อผิดพลาดที่อาจเกิดจากการหล่อประเภท)
ตัวอย่างเช่น:
arraylist list = new ArrayList (); list.add (จำนวนเต็มใหม่ (3)); list.add (จำนวนเต็มใหม่ (4)); int i = ((จำนวนเต็ม) (list.get (0))). parseInt ();
ลำบากมาก
arrayList <integer> list = new ArrayList <integer> (); list.add (จำนวนเต็มใหม่ (3)); list.add (จำนวนเต็มใหม่ (4)); int i = list.get (0) .parseint ();
2 การบรรจุอัตโนมัติ/ยกเลิกกล่อง
ประโยคสุดท้ายของตัวอย่างข้างต้นสามารถเปลี่ยนเป็น:
การคัดลอกรหัสมีดังนี้: int i = list.get (0);
เนื่องจากประเภทดั้งเดิมและคลาส wrapper ที่สอดคล้องกันไม่จำเป็นต้องแปลงอย่างชัดเจน
3 สำหรับการสอบ
การเพิ่มประสิทธิภาพของลูป
int a [] = {...... }; // เริ่มต้นสำหรับ (int i: a) {...... }อย่าใช้ i = 0 ก่อนหน้านี้ i <a.length; i ++
4 นำเข้าแบบคงที่
java.math ที่ปรับก่อนหน้านี้
การคัดลอกรหัสมีดังนี้: math.sqrt ();
ตอนนี้นำเข้าแบบคงที่ java.lang.math.sqrt;
sqrt ();
เทียบเท่ากับการมีวิธีนี้ในชั้นเรียนของคุณเอง
5 พารามิเตอร์ความยาวตัวแปร
int sum (int ... intlist) {int sum; ผลรวม = 0; สำหรับ (int i = 0; i <intlist.length; i ++) {sum+= intlist [i]; } return sum;}มีพารามิเตอร์ใด ๆ ถือเป็นอาร์เรย์
★คุณสมบัติใหม่ของ JDK6.0
ปรับปรุงสำหรับการระบุคำอธิบายประกอบคำสั่งลูปของพารามิเตอร์แบบสแตติกแบบคงที่ "ซ่อนเร้น" (VARARG)
ไวด์การ์ดและความแปรปรวนร่วมที่ได้รับการปรับปรุงสำหรับคำสั่งวนรอบ
ในการวนซ้ำชุดและอาร์เรย์การปรับปรุงสำหรับลูปจะให้ไวยากรณ์ที่ใช้งานง่ายและเข้ากันได้ มีสองจุดที่ควรค่าแก่การกล่าวถึง:
1. ในลูปนิพจน์การเริ่มต้นจะคำนวณเพียงครั้งเดียว นิพจน์ int
ไม่ได้รับการปรับปรุงสำหรับ:
int sum = 0; จำนวนเต็ม [] ตัวเลข = computenumbers (); สำหรับ (int i = 0; i <number.length; i ++) sum+= numbers [i];
ปรับปรุงสำหรับ:
int sum = 0; สำหรับ (หมายเลข int: computenumbers ()) sum += number;
ข้อ จำกัด
ไม่สามารถเข้าถึงตัววนซ้ำหรือตัวห้อยระหว่างการปรับปรุงการวนซ้ำได้
โปรดดูตัวอย่างต่อไปนี้:
สำหรับ (int i = 0; i <number.length; i ++) {ถ้า (i! = 0) system.out.print (","); system.out.print (ตัวเลข [i]);}นี่เป็นอีกตัวอย่างหนึ่ง:
สำหรับ (iterator <จำนวนเต็ม> it = n.iterator (); it.hasnext ();) ถ้า (it.next () <0) it.remove ();
ความเห็น
การประมวลผลความคิดเห็นเป็นหัวข้อใหญ่ เนื่องจากบทความนี้มุ่งเน้นเฉพาะคุณสมบัติภาษาหลักเราจึงไม่ได้ตั้งใจจะครอบคลุมรูปแบบและข้อผิดพลาดที่เป็นไปได้ทั้งหมด เราจะหารือเกี่ยวกับคำอธิบายประกอบในตัว (Suppresswarnings, เลิกจ้างและแทนที่) และข้อ จำกัด ของการประมวลผลคำอธิบายประกอบทั่วไป
ระงับคำเตือน
ความคิดเห็นนี้จะปิดการเตือนคอมไพเลอร์ในระดับชั้นเรียนหรือวิธีการ บางครั้งคุณรู้ชัดเจนกว่าคอมไพเลอร์ว่ารหัสจะต้องใช้วิธีการปฏิเสธหรือดำเนินการบางอย่างที่ไม่สามารถระบุได้อย่างคงที่ว่าประเภทที่ปลอดภัยนั้นปลอดภัยและใช้:
@suppresswarnings ("การเสื่อมสภาพ") โมฆะคงที่สาธารณะคงที่ selfdestruct () {thread.currentthread (). stop ();};นี่อาจเป็นสิ่งที่มีประโยชน์ที่สุดเกี่ยวกับคำอธิบายประกอบในตัว น่าเสียดายที่ Javac สำหรับ 1.5.0_04 ไม่รองรับ แต่ 1.6 รองรับมันและซันกำลังดำเนินการในการพอร์ตมันย้อนกลับไปที่ 1.5
คำอธิบายประกอบนี้รองรับใน Eclipse 3.1 และ IDE อื่น ๆ อาจรองรับ สิ่งนี้ช่วยให้คุณสามารถปลดปล่อยรหัสจากคำเตือนได้อย่างสมบูรณ์ หากมีคำเตือนในเวลาคอมไพล์คุณสามารถมั่นใจได้ว่าคุณเพิ่งเพิ่ม - เพื่อช่วยดูรหัสที่อาจไม่ปลอดภัย ด้วยการเพิ่มของยาชื่อสามัญมันจะมีประโยชน์มากขึ้นในการใช้งาน
ซึ่งเลิกใช้แล้ว
น่าเสียดายที่การเลิกกิจการนั้นไม่ได้มีประโยชน์ เดิมทีตั้งใจจะแทนที่แท็ก @Deprecated Javadoc แต่เนื่องจากไม่มีฟิลด์ใด ๆ จึงไม่มีวิธีที่จะแนะนำผู้ใช้คลาสหรือวิธีการที่เลิกใช้แล้วควรใช้แทน ที่สุด
ทั้งการใช้งานต้องใช้แท็ก Javadoc และคำอธิบายประกอบนี้
แทนที่
Override กล่าวว่าวิธีการที่บันทึกย่อควรแทนที่วิธีการด้วยลายเซ็นเดียวกันใน superclass:
@Overridepublic int hashCode () {... }เมื่อดูตัวอย่างข้างต้นหาก "C" ไม่ได้เป็นตัวพิมพ์ใหญ่ใน HashCode จะไม่มีข้อผิดพลาดในเวลาที่รวบรวม แต่วิธีการจะไม่ถูกเรียกตามที่คาดไว้เมื่อรันไทม์ โดยการเพิ่มแท็กแทนที่คอมไพเลอร์จะแจ้งให้ทราบหากดำเนินการเขียนซ้ำ
สิ่งนี้ยังมีประโยชน์ในสถานการณ์ที่ superclasses เปลี่ยนไป หากมีการเพิ่มพารามิเตอร์ใหม่ลงในวิธีการและวิธีการเองถูกเปลี่ยนชื่อตัวย่อยจะไม่ถูกรวบรวมอย่างกะทันหันเพราะมันไม่ได้เขียนอะไรอีกต่อไปจาก superclass อีกต่อไป
บันทึกอื่น ๆ
ความคิดเห็นมีประโยชน์มากในสถานการณ์อื่น ๆ เมื่อมันไม่ได้ปรับเปลี่ยนพฤติกรรมโดยตรง แต่ปรับปรุงพฤติกรรมโดยเฉพาะอย่างยิ่งเมื่อเพิ่มรหัสหม้อไอน้ำคำอธิบายประกอบทำงานได้ดีมากในกรอบงานเช่น EJB และบริการเว็บ
ความคิดเห็นไม่สามารถใช้เป็นตัวประมวลผลล่วงหน้า การออกแบบของซันป้องกันการปรับเปลี่ยนไบต์ของคลาสโดยเฉพาะเนื่องจากความคิดเห็น สิ่งนี้ช่วยให้คุณเข้าใจผลลัพธ์ของภาษาได้อย่างถูกต้องและเครื่องมือเช่น IDE ยังสามารถทำการวิเคราะห์รหัสเชิงลึกและ refactoring
ความคิดเห็นไม่ใช่กระสุนเงิน เมื่อฉันพบมันครั้งแรกผู้คนพยายามลองใช้เทคนิคต่าง ๆ โปรดดูคำแนะนำต่อไปนี้ที่ได้รับจากผู้อื่น:
คลาสสาธารณะ foo {@propertyprivate int bar;}แนวคิดคือการสร้างวิธีการ getter และ setter โดยอัตโนมัติสำหรับแถบฟิลด์ส่วนตัว น่าเสียดายที่มีสองความล้มเหลวในแนวคิดนี้: 1) มันไม่ทำงานและ 2) มันทำให้รหัสอ่านและประมวลผลยาก มันเป็นไปไม่ได้ที่จะนำไปใช้เพราะดังที่ได้กล่าวไว้ก่อนหน้านี้ Sun ป้องกันการดัดแปลงคลาสด้วยความคิดเห็นโดยเฉพาะ
แม้ว่าจะเป็นไปได้ แต่ก็ไม่ใช่ความคิดที่ดีเพราะมันทำให้รหัสอ่านไม่ดี ครั้งแรกที่คุณเห็นรหัสนี้คุณจะไม่ทราบว่าความคิดเห็นสร้างวิธีการ นอกจากนี้หากคุณต้องการดำเนินการบางอย่างภายในวิธีการเหล่านี้ในอนาคตความคิดเห็นนั้นไร้ประโยชน์ ในระยะสั้นอย่าพยายามทำสิ่งที่รหัสปกติสามารถทำกับความคิดเห็นได้
แจกแจง
Enum เป็นเหมือนการประกาศ Int สุดท้ายของสาธารณะซึ่งถูกใช้เป็นค่า enum เป็นเวลาหลายปี การปรับปรุงที่ใหญ่ที่สุดและชัดเจนที่สุดใน INT คือความปลอดภัยประเภท - คุณไม่สามารถแทนที่ประเภทอื่นด้วยการแจงนับประเภทหนึ่งซึ่งแตกต่างจาก INT และ INT ทั้งหมดจะเหมือนกันสำหรับคอมไพเลอร์ ด้วยข้อยกเว้นน้อยมากโครงสร้าง int สไตล์ enum ทั้งหมดควรถูกแทนที่ด้วยอินสแตนซ์ enum
การแจงนับมีคุณสมบัติเพิ่มเติมบางอย่าง ทั้งสองคลาสที่ใช้งานได้จริง enummap และ enumset คือการใช้งานชุดมาตรฐานที่ได้รับการปรับให้เหมาะสมเป็นพิเศษสำหรับการแจงนับ หากคุณรู้ว่าคอลเลกชันมีเฉพาะประเภท enum คุณควรใช้คอลเลกชันพิเศษเหล่านี้แทน HashMap หรือ Hashset
ในกรณีส่วนใหญ่คุณสามารถใช้ enum เพื่อแทนที่ int สุดท้ายคงที่สาธารณะทั้งหมดในรหัส พวกเขาเทียบเคียงได้และสามารถนำเข้าแบบคงที่ดังนั้นการอ้างอิงถึงพวกเขาดูเหมือนจะเทียบเท่าแม้กระทั่งสำหรับคลาสภายใน (หรือประเภท enum ภายใน) โปรดทราบว่าเมื่อเปรียบเทียบประเภทของ enum คำแนะนำที่ประกาศพวกเขาระบุค่าตามลำดับของพวกเขา
วิธีการคงที่ "ซ่อน"
สองวิธีคงที่ปรากฏในการประกาศประเภท enum ทั้งหมด เนื่องจากพวกเขาเป็นวิธีการคงที่ในคลาสย่อย enum ไม่ใช่วิธีการของ enum ตัวเองพวกเขาไม่ปรากฏใน javadoc ของ java.lang.enum
ครั้งแรกคือค่า () ซึ่งส่งคืนอาร์เรย์ของค่าที่เป็นไปได้ทั้งหมดของประเภทการแจงนับ
ที่สองคือ valueof () ซึ่งส่งคืนประเภท enum สำหรับสตริงที่ให้ไว้ซึ่งจะต้องตรงกับการประกาศซอร์สโค้ด
วิธี
หนึ่งในแง่มุมที่เราโปรดปรานเกี่ยวกับประเภท enum คือมันสามารถมีวิธีการ ในอดีตคุณอาจต้องเขียนโค้ดเพื่อแปลง INT สุดท้ายคงที่สาธารณะและแปลงจากประเภทฐานข้อมูลเป็น URL JDBC ตอนนี้ประเภท enum เองสามารถทำได้ด้วยความสมบูรณ์
วิธีการจัดการรหัส นี่คือตัวอย่างรวมถึงวิธีนามธรรมของประเภทข้อมูล enum และการใช้งานที่มีให้ในแต่ละอินสแตนซ์ enum:
Public Enum databasetype {Oracle {Public String getJdBcurl () {... }}, mysql {สตริงสาธารณะ getjdbcurl () {... }}; สตริงนามธรรมสาธารณะ getjdbcurl ();};};ตอนนี้ประเภท enum สามารถให้วิธีการปฏิบัติโดยตรง ตัวอย่างเช่น:
Databasetype dbType = ... ; สตริง jdbcurl = dbtype.getjdbcurl ();
ในการรับ URL คุณต้องรู้ล่วงหน้าว่าวิธีการยูทิลิตี้อยู่ที่ไหน
วาร์ก
การใช้พารามิเตอร์ที่เปลี่ยนแปลงได้อย่างถูกต้องจะทำความสะอาดรหัสขยะบางอย่าง ตัวอย่างทั่วไปคือวิธีการบันทึกที่มีจำนวนตัวแปรของพารามิเตอร์สตริง:
log.log (รหัสสตริง) log.log (รหัสสตริง, สตริง arg) log.log (รหัสสตริง, สตริง arg1, สตริง arg2) log.log (รหัสสตริง, สตริง [] args)
เมื่อพูดถึงพารามิเตอร์ตัวแปรเป็นที่น่าสนใจว่าหากคุณแทนที่ตัวอย่างสี่ตัวอย่างแรกด้วยพารามิเตอร์ตัวแปรใหม่มันจะเข้ากันได้:
คัดลอกรหัสดังต่อไปนี้: log.log (รหัสสตริง, สตริง ... args)
พารามิเตอร์ที่ไม่แน่นอนทั้งหมดนั้นเข้ากันได้กับแหล่งที่มา - นั่นคือถ้าโปรแกรมการเรียกทั้งหมดของวิธีการบันทึก () มีการคอมไพล์ใหม่วิธีทั้งสี่สามารถแทนที่ได้โดยตรง อย่างไรก็ตามหากจำเป็นต้องใช้ความเข้ากันได้แบบไบนารีย้อนหลังแล้วสามวิธีแรกจะต้องถูกยกเลิก เฉพาะวิธีสุดท้ายที่มีพารามิเตอร์สตริงอาร์เรย์เทียบเท่ากับรุ่น variadic ดังนั้นจึงสามารถแทนที่ด้วยเวอร์ชัน Variadic
พิมพ์
หากคุณต้องการให้ผู้โทรรู้ว่าควรใช้พารามิเตอร์ประเภทใดคุณควรหลีกเลี่ยงประเภทการหล่อด้วยพารามิเตอร์ที่ไม่แน่นอน ดูตัวอย่างต่อไปนี้ความหวังแรกคือสตริงและความหวังที่สองคือข้อยกเว้น:
log.log (วัตถุ ... วัตถุ) {สตริงข้อความ = (สตริง) วัตถุ [0]; ถ้า (objects.length> 1) {ข้อยกเว้น e = (ข้อยกเว้น) วัตถุ [1]; // ทำบางสิ่งที่มีข้อยกเว้น}} ลายเซ็นวิธีการควรมีดังนี้และพารามิเตอร์ที่ไม่แน่นอนที่สอดคล้องกันจะถูกประกาศโดยใช้สตริงและข้อยกเว้นตามลำดับ:
การคัดลอกรหัสมีดังนี้: log.log (ข้อความสตริง, ข้อยกเว้น e, วัตถุ ... วัตถุ) {... }
อย่าใช้พารามิเตอร์ตัวแปรเพื่อทำลายระบบประเภท มันสามารถใช้ได้เฉพาะเมื่อมีการพิมพ์อย่างมาก สำหรับกฎนี้ printstream.printf () เป็นข้อยกเว้นที่น่าสนใจ: มันให้ข้อมูลประเภทเป็นอาร์กิวเมนต์แรกเพื่อให้ประเภทเหล่านั้นสามารถยอมรับได้ในภายหลัง
ผลตอบแทนความแปรปรวนร่วม
การใช้งานขั้นพื้นฐานของการกลับมาของ covariant คือการหลีกเลี่ยงการคัดเลือกนักแสดงเมื่อประเภทการส่งคืนของการใช้งานเป็นที่รู้จักกันว่าเฉพาะเจาะจงมากกว่า API ในตัวอย่างต่อไปนี้มีอินเทอร์เฟซสวนสัตว์ที่ส่งคืนวัตถุสัตว์ การดำเนินการของเราส่งคืนวัตถุสัตว์ IMPL แต่ก่อน JDK 1.5 จะต้องมีการประกาศให้คืนวัตถุสัตว์ -
Zoo Interface Public {Public Animal Getanimal ();} คลาสสาธารณะ Zooimpl ใช้ Zoo {Public Animal Getanimal () {return New Animalimpl ();}}การใช้ผลตอบแทน covariant แทนที่การต่อต้านสามรูปแบบ:
การเข้าถึงภาคสนามโดยตรง เพื่อหลีกเลี่ยงข้อ จำกัด ของ API การใช้งานบางอย่างจะเปิดเผยคลาสย่อยลงในฟิลด์โดยตรง:
คัดลอกรหัสดังนี้: zooimpl._animal
อีกรูปแบบหนึ่งคือการแปลงลงในโปรแกรมการโทรโดยรู้ว่าการใช้งานนั้นเป็นคลาสย่อยที่เฉพาะเจาะจง:
การคัดลอกรหัสมีดังนี้: ((AnimalImpl) zooimpl.getanimal ()). implmethod ();
แบบฟอร์มสุดท้ายที่ฉันเคยเห็นเป็นวิธีคอนกรีตที่ใช้เพื่อหลีกเลี่ยงปัญหาที่เกิดจากลายเซ็นที่แตกต่างอย่างสิ้นเชิง:
การคัดลอกรหัสมีดังนี้: zooimpl._getanimal ();
ทั้งสามโหมดนี้มีปัญหาและข้อ จำกัด ไม่ว่าจะไม่เรียบร้อยเพียงพอหรือเปิดเผยรายละเอียดการใช้งานที่ไม่จำเป็น
ความแปรปรวนร่วม
โหมดการส่งคืน covariant นั้นสะอาดกว่าปลอดภัยและง่ายต่อการบำรุงรักษาและไม่จำเป็นต้องใช้ประเภทการหล่อหรือวิธีการเฉพาะหรือฟิลด์:
Public Animalimpl getanimal () {return imentimpl ใหม่ (); - ใช้ผลลัพธ์:
การคัดลอกรหัสมีดังนี้: zooimpl.getanimal (). implmethod ();
ใช้ยาชื่อสามัญ
เราจะเรียนรู้เกี่ยวกับยาสามัญจากสองมุมมอง: การใช้ยาสามัญและการสร้างยาชื่อสามัญ เราไม่ได้หารือเกี่ยวกับการใช้งานรายการชุดและแผนที่ที่ชัดเจน มันเพียงพอที่จะรู้ว่าคอลเลกชันทั่วไปมีประสิทธิภาพและควรใช้บ่อย
เราจะหารือเกี่ยวกับการใช้วิธีการทั่วไปและวิธีการของคอมไพเลอร์ในการอนุมานประเภท โดยปกติแล้วสิ่งเหล่านี้จะไม่ผิดพลาด แต่เมื่อมีบางอย่างผิดพลาดข้อความแสดงข้อผิดพลาดอาจทำให้เกิดความสับสนมากดังนั้นคุณต้องรู้วิธีแก้ไขปัญหาเหล่านี้
วิธีการทั่วไป
นอกเหนือจากประเภททั่วไปแล้ว Java 5 ยังแนะนำวิธีการทั่วไป ในตัวอย่างนี้จาก java.util.collections รายการองค์ประกอบเดียวจะถูกสร้างขึ้น ประเภทองค์ประกอบของรายการใหม่จะอนุมานตามประเภทของวัตถุที่ส่งผ่านในวิธีการ:
คัดลอกรหัสดังต่อไปนี้: คงที่ <t> รายการ <t> คอลเลกชัน SingletonList (t o)
ตัวอย่างการใช้งาน:
รายการสาธารณะ <Integer> getListofone () {return collections.singletonList (1);}ในการใช้งานตัวอย่างเราผ่าน int ดังนั้นประเภทการส่งคืนของวิธีการคือรายการ <จำนวนเต็ม> คอมไพเลอร์ infers t ถึงจำนวนเต็ม สิ่งนี้แตกต่างจากประเภททั่วไปเพราะคุณไม่จำเป็นต้องระบุประเภทพารามิเตอร์อย่างชัดเจน
นอกจากนี้ยังแสดงให้เห็นถึงการมีปฏิสัมพันธ์ระหว่าง autoboxing และ Generics พารามิเตอร์ประเภทจะต้องเป็นประเภทอ้างอิง: นั่นคือเหตุผลที่เราได้รับรายการ <จำนวนเต็ม> แทนที่จะเป็นรายการ <int>
วิธีการทั่วไปโดยไม่มีพารามิเตอร์
วิธีการ emptylist () ได้รับการแนะนำด้วยยาชื่อสามัญว่าเป็นการเปลี่ยนรูปแบบที่ปลอดภัยของฟิลด์ emport_list ใน java.util.collections:
คัดลอกรหัสดังนี้: คงที่ <t> รายการ <t> collections.empylist ()
ตัวอย่างการใช้งาน:
รายการสาธารณะ <จำนวนเต็ม> getNointeGers () {return collections.empylist ();}ซึ่งแตกต่างจากตัวอย่างก่อนหน้านี้วิธีนี้ไม่มีพารามิเตอร์ดังนั้นคอมไพเลอร์จะอนุมานประเภทของ T ได้อย่างไร โดยพื้นฐานแล้วมันจะพยายามใช้พารามิเตอร์หนึ่งครั้ง หากไม่ได้ผลก็จะพยายามใช้ประเภทการส่งคืนหรือการมอบหมายอีกครั้ง ในตัวอย่างนี้ผลตอบแทนคือรายการ <จำนวนเต็ม> ดังนั้น t ถูกอนุมานเป็นจำนวนเต็ม
จะเกิดอะไรขึ้นหากมีการเรียกใช้วิธีการทั่วไปนอกคำสั่ง Return หรือ Assignment จากนั้นคอมไพเลอร์จะไม่สามารถทำการถ่ายโอนประเภทที่สอง ในตัวอย่างต่อไปนี้ emgthylist () ถูกเรียกจากภายในตัวดำเนินการตามเงื่อนไข:
รายการสาธารณะ <Integer> getNointeGers () {return x? collections.empylist (): null;} เนื่องจากคอมไพเลอร์ไม่สามารถมองเห็นบริบทการส่งคืนและไม่สามารถอนุมานได้จึงเป็นการละทิ้งและใช้วัตถุ คุณจะเห็นข้อความแสดงข้อผิดพลาดเช่น: "ไม่สามารถแปลงรายการ <jobch> เป็นรายการ <จำนวนเต็ม>"
ในการแก้ไขข้อผิดพลาดนี้ควรส่งผ่านพารามิเตอร์พิมพ์อย่างชัดเจนไปยังการเรียกใช้วิธีการ ด้วยวิธีนี้คอมไพเลอร์จะไม่พยายามอนุมานพารามิเตอร์ประเภทและสามารถรับผลลัพธ์ที่ถูกต้อง:
การคัดลอกรหัสมีดังนี้: return x? คอลเลกชัน <teger> emptylist (): null;
สถานที่อื่นที่สิ่งนี้เกิดขึ้นบ่อยครั้งคือการเรียกวิธีการ หากเมธอดใช้พารามิเตอร์รายการ <String> และจำเป็นต้องเรียกใช้ emgthylist ที่ผ่าน () สำหรับพารามิเตอร์นั้นต้องใช้ไวยากรณ์นี้ด้วย
นอกคอลเลกชัน
นี่คือสามตัวอย่างของประเภททั่วไปซึ่งไม่ใช่คอลเลกชัน แต่ใช้ยาชื่อสามัญในรูปแบบใหม่ ตัวอย่างทั้งสามมาจากห้องสมุด Java มาตรฐาน:
คัดลอกรหัสดังนี้: คลาส <t>
คลาสถูกกำหนดพารามิเตอร์ในประเภทคลาส สิ่งนี้ทำให้เป็นไปได้ที่จะสร้าง Newinstance โดยไม่มีการหล่อแบบ
คัดลอกรหัสดังนี้: เทียบเท่า <t>
เปรียบเทียบได้คือพารามิเตอร์โดยประเภทการเปรียบเทียบจริง สิ่งนี้ให้การพิมพ์ที่แข็งแกร่งขึ้นเมื่อเปรียบเทียบกับการโทร () ตัวอย่างเช่นสตริงใช้งานเทียบเท่า <string> การเรียก compereto () ในสิ่งอื่นนอกเหนือจากสตริงจะล้มเหลวในเวลารวบรวม
คัดลอกรหัสดังนี้: enum <e ขยาย enum <e >>
enum ถูกพารามิเตอร์โดยประเภท enum ประเภท enum ที่ชื่อว่าสีจะขยาย enum <color> เมธอด getDeclaringClass () ส่งคืนวัตถุประเภทการแจงนับในตัวอย่างนี้วัตถุสี มันแตกต่างจาก getClass () ซึ่งอาจส่งคืนคลาสนิรนาม
ไพ่
ส่วนที่ซับซ้อนที่สุดของทั่วไปคือความเข้าใจในตัวละครไวลด์การ์ด เราจะหารือเกี่ยวกับไวด์การ์ดสามประเภทและการใช้งานของพวกเขา
ก่อนอื่นเรามาเข้าใจกันว่าอาร์เรย์ทำงานอย่างไร คุณสามารถกำหนดค่าจากจำนวนเต็ม [] ให้กับตัวเลข [] หากคุณพยายามเขียนหมายเลขลอยไปยังหมายเลข [] มันสามารถรวบรวมได้ แต่มันจะล้มเหลวเมื่อรันไทม์และ ArrayStorEException จะปรากฏขึ้น:
จำนวนเต็ม [] ia = จำนวนเต็มใหม่ [5]; จำนวน [] na = ia; na [0] = 0.5; // คอมไพล์ แต่ล้มเหลวเมื่อรันไทม์
หากคุณพยายามแปลงตัวอย่างเป็นทั่วไปโดยตรงมันจะล้มเหลวในเวลารวบรวมเนื่องจากไม่อนุญาตให้มีการมอบหมาย:
รายการ <จำนวนเต็ม> ilist = new ArrayList <Integer> (); รายการ <umber> nlist = ilist; // ไม่อนุญาตให้ใช้ Nlist.add (0.5);
หากคุณใช้ Generics คุณจะไม่พบ Runtime ClasscastException ตราบใดที่รหัสไม่ปรากฏขึ้นเมื่อรวบรวม
ไวด์การ์ดขีด จำกัด บน
สิ่งที่เราต้องการคือรายการของประเภทองค์ประกอบที่ไม่รู้จักซึ่งแตกต่างจากอาร์เรย์
รายการ <number> เป็นรายการที่ประเภทองค์ประกอบคือหมายเลขประเภทเฉพาะ
รายการ <? ขยายหมายเลข> เป็นรายการของประเภทองค์ประกอบที่ไม่รู้จัก มันเป็นจำนวนหรือชนิดย่อย
ขีด จำกัด บน
หากเราอัปเดตตัวอย่างเริ่มต้นและกำหนดค่าเป็นรายการ <? ขยายหมายเลข> จากนั้นการมอบหมายจะสำเร็จในขณะนี้:
รายการ <จำนวนเต็ม> ilist = new ArrayList <integer> (); รายการ <? ขยายหมายเลข> nlist = ilist; number n = nlist.get (0); nlist.add (0.5); // ไม่อนุญาต
เราสามารถรับหมายเลขจากรายการได้เพราะเราสามารถกำหนดให้หมายเลขโดยไม่คำนึงถึงประเภทองค์ประกอบที่แน่นอนของรายการ (ลอย, จำนวนเต็มหรือหมายเลข)
เรายังไม่สามารถแทรกประเภทจุดลอยตัวลงในรายการได้ สิ่งนี้จะล้มเหลวในเวลาคอมไพล์เพราะเราไม่สามารถพิสูจน์ได้ว่าสิ่งนี้ปลอดภัย หากเราต้องการเพิ่มประเภทจุดลอยตัวลงในรายการมันจะทำลายความปลอดภัยประเภทเริ่มต้นของ ILIST - มันจะเก็บเป็นจำนวนเต็มเท่านั้น
ไวด์การ์ดทำให้เรามีพลังที่แสดงออกได้มากกว่าอาร์เรย์
ทำไมต้องใช้ไวด์การ์ด
ในตัวอย่างต่อไปนี้ WildCards ใช้เพื่อซ่อนข้อมูลประเภทจากผู้ใช้ API ภายในชุดจะถูกเก็บไว้เป็นลูกค้า IMPL ผู้ใช้ API เท่านั้นที่รู้ว่าพวกเขาได้รับชุดที่พวกเขาสามารถอ่านลูกค้าได้
ต้องใช้สัญลักษณ์แทนที่นี่เพราะเป็นไปไม่ได้ที่จะกำหนดค่าให้ตั้งค่า <ลูกค้า> จาก Set <CustomerImpl>:
Public Class CustomerFactory {Private Set <CustomerImpl> _customers; ชุดสาธารณะ <? ขยายลูกค้า> getCustomers () {return _Customers;}}ไวด์การ์ดและผลตอบแทน Covariant
การใช้งานทั่วไปของตัวละครไวลด์การ์ดคือการใช้กับการกลับมาของ Covariant กฎเดียวกันกับการมอบหมายสามารถนำไปใช้กับผลตอบแทน covariant หากคุณต้องการส่งคืนประเภททั่วไปที่เฉพาะเจาะจงมากขึ้นในวิธีการเขียนใหม่วิธีที่ประกาศจะต้องใช้ตัวแทน:
NumberGenerator ส่วนต่อประสานสาธารณะ {รายการสาธารณะ <? ขยายหมายเลข> สร้าง ();} คลาสสาธารณะ FIBONACCIGENERATOR ขยาย NumberGenerator {รายการสาธารณะ <Integer> สร้าง () {... }}หากคุณต้องการใช้อาร์เรย์อินเทอร์เฟซสามารถส่งคืนหมายเลข [] ในขณะที่การใช้งานสามารถส่งคืนจำนวนเต็ม []
ขีด จำกัด ล่าง
สิ่งที่เรากำลังพูดถึงส่วนใหญ่เกี่ยวกับ Wildcard ขีด จำกัด บน นอกจากนี้ยังมีไวด์การ์ดขีด จำกัด ที่ต่ำกว่า รายการ <? Super Number> เป็นรายการของ "ประเภทองค์ประกอบ" ที่แน่นอนไม่ทราบ แต่อาจเป็น mnumber หรือจำนวน supertype ดังนั้นจึงอาจเป็นรายการ <gumber> หรือรายการ <Ojrop>
ไวด์การ์ดที่มีข้อ จำกัด ต่ำกว่านั้นพบได้น้อยกว่าไวด์การ์ดที่ จำกัด แต่พวกเขาจำเป็นต้องใช้เมื่อจำเป็น
ขีด จำกัด ล่างและบน
รายการ <? ขยายหมายเลข> readlist = arrayList ใหม่ <integer> (); number n = readlist.get (0); รายการ <? Super Number> writeList = new ArrayList <Object> (); writelist.add (จำนวนเต็มใหม่ (5));
อย่างแรกคือรายการตัวเลขที่สามารถอ่านได้จาก
อย่างที่สองคือรายการตัวเลขที่คุณสามารถเขียนได้
ไวด์การ์ดที่ไม่มีขอบเขต
ในที่สุดเนื้อหาของรายการ <?> รายการสามารถเป็นประเภทใดก็ได้และเกือบจะเหมือนกับรายการ <? ขยายวัตถุ> วัตถุสามารถอ่านได้ตลอดเวลา แต่ไม่สามารถเขียนเนื้อหาไปยังรายการได้
ไวด์การ์ดใน API สาธารณะ
ในระยะสั้นดังที่ได้กล่าวไว้ก่อนหน้านี้ไวด์การ์ดมีความสำคัญมากในการซ่อนรายละเอียดการใช้งานจากผู้โทร แต่ถึงแม้ว่าสัญลักษณ์ด้านล่างของสัญลักษณ์ด้านล่างดูเหมือนจะให้การเข้าถึงแบบอ่านอย่างเดียวพวกเขาไม่ใช่กรณีเนื่องจากวิธีการที่ไม่ใช่ทางพันธุกรรมเช่นลบ (ตำแหน่ง int) หากคุณต้องการคอลเลกชันที่ไม่เปลี่ยนแปลงอย่างแท้จริงคุณสามารถใช้วิธีการบน java.util.collection เช่น UnmodifiableList ()
จดจำ Wildcards เมื่อเขียน API โดยทั่วไปเมื่อผ่านประเภททั่วไปคุณควรลองใช้ไวด์การ์ด ช่วยให้ผู้โทรเข้าถึง API ได้มากขึ้น
โดยรับรายการ <? ขยายหมายเลข> แทนรายการ <gumber> วิธีการต่อไปนี้สามารถเรียกได้ด้วยรายการหลายประเภท:
การคัดลอกรหัสมีดังนี้: โมฆะ removenegatives (รายการ <? ขยายหมายเลข> รายการ);
สร้างประเภททั่วไป
ตอนนี้เราจะหารือเกี่ยวกับการสร้างประเภททั่วไปของเราเอง เราจะแสดงตัวอย่างที่ความปลอดภัยประเภทสามารถปรับปรุงได้โดยใช้ยาชื่อสามัญและเราจะหารือเกี่ยวกับปัญหาทั่วไปบางอย่างเมื่อใช้ประเภททั่วไป
ฟังก์ชั่นคอลเลกชัน
ตัวอย่างแรกของคลาสทั่วไปคือตัวอย่างของสไตล์การรวบรวม คู่มีพารามิเตอร์สองประเภทและฟิลด์เป็นอินสแตนซ์ของประเภท:
คู่สุดท้ายระดับสุดท้าย <a, b> {สาธารณะสุดท้ายเป็นครั้งแรก; Public Final B Second; คู่สาธารณะ (A First, B Second) {this.first = แรก;สิ่งนี้ทำให้สามารถส่งคืนสองรายการจากวิธีการโดยไม่ต้องเขียนคลาสเฉพาะสำหรับการรวมกันของสองประเภท อีกวิธีหนึ่งคือการส่งคืนวัตถุ [] ซึ่งเป็นประเภทที่ไม่ปลอดภัยหรือไม่เป็นระเบียบ
ในการใช้งานต่อไปนี้เราส่งคืนไฟล์และบูลีนจากวิธีการ ไคลเอนต์ของวิธีการสามารถใช้ฟิลด์โดยตรงโดยไม่ต้องเลือกประเภท:
คู่สาธารณะ <ไฟล์, บูลีน> getFileAndWriteStatus (พา ธ สตริง) {// สร้างไฟล์และสถานะกลับคู่ใหม่ <ไฟล์, บูลีน> (ไฟล์, สถานะ);} คู่ <ไฟล์, บูลีน> ผลลัพธ์ = getFileAndWriteStatus ("... "); ไฟล์ f = result.first; บูลีนเขียนได้ = result.second;นอกคอลเลกชัน
ในตัวอย่างต่อไปนี้มีการใช้ยาสามัญเพื่อความปลอดภัยในการรวบรวมเวลาเพิ่มเติม โดยการกำหนดพารามิเตอร์คลาส DBFactory ไปยังประเภทเพียร์ที่สร้างขึ้นจริง ๆ แล้วคุณกำลังบังคับให้คลาสย่อยจากโรงงานส่งคืนชนิดย่อยเฉพาะของเพียร์:
บทคัดย่อระดับสาธารณะ dbfactory <t ขยาย dbpeer> {protected abstract t createSptyPeer (); รายการสาธารณะ <t> รับ (ข้อ จำกัด ของสตริง) {รายการ <t> peers = new ArrayList <T> (); // ฐานข้อมูล MagicReturn Peers;}}}}}}}โดยการใช้ dbfactory <custrie> ลูกค้าจะต้องส่งคืนลูกค้าจาก createSpTypeer ()::
Public Class CustomerFactory ขยาย dbfactory <custrie> {ลูกค้า Public CreateSptyPeer () {ส่งคืนลูกค้าใหม่ ();}}วิธีการทั่วไป
ไม่ว่าคุณต้องการกำหนดข้อ จำกัด เกี่ยวกับประเภททั่วไประหว่างพารามิเตอร์และระหว่างพารามิเตอร์และประเภทการส่งคืนคุณสามารถใช้วิธีการทั่วไป:
ตัวอย่างเช่นหากฟังก์ชั่นการผกผันที่เขียนกลับเป็นตำแหน่งอาจไม่จำเป็นต้องใช้วิธีการทั่วไป อย่างไรก็ตามหากคุณต้องการให้การผกผันส่งคืนรายการใหม่คุณอาจต้องการให้ประเภทองค์ประกอบของรายการใหม่เป็นเช่นเดียวกับประเภทของรายการขาเข้า ในกรณีนี้จำเป็นต้องใช้วิธีการทั่วไป:
คัดลอกรหัสดังนี้: <t> รายการ <t> ย้อนกลับ (รายการ <t> รายการ)
คอนกรีต
เมื่อใช้คลาสทั่วไปคุณอาจต้องการสร้างอาร์เรย์ t [] เนื่องจากไม่อนุญาตให้ใช้ยาชื่อสามัญจึงไม่ได้รับอนุญาต
คุณสามารถลองคัดเลือกนักแสดง [] ถึง t [] แต่สิ่งนี้ไม่ปลอดภัย
โซลูชั่นคอนกรีต
ตามอนุสัญญาของบทเรียนทั่วไปโซลูชันใช้ "โทเค็นประเภท" โดยการเพิ่มพารามิเตอร์ระดับ <t> ลงในตัวสร้างคุณสามารถบังคับให้ไคลเอนต์จัดเตรียมวัตถุคลาสที่ถูกต้องสำหรับพารามิเตอร์ประเภทของคลาส:
คลาสสาธารณะ ArrayExample <T> {คลาสส่วนตัว <t> clazz; public ArrayExample (คลาส <t> clazz) {this.clazz = clazz;} สาธารณะ t [] getarray (ขนาด int) {return (t []) array.newinstance (clazz, ขนาด);}}}}}}ในการสร้าง ArrayExample <String> ไคลเอนต์จะต้องส่งสตริงไปยังคอนสตรัคเตอร์เนื่องจากประเภทของสตริงคลาสคือคลาส <string>
การมีวัตถุคลาสทำให้สามารถสร้างกลุ่มของประเภทองค์ประกอบที่ถูกต้องได้
ฉันหวังว่าบทความนี้จะเป็นประโยชน์กับการเขียนโปรแกรม Java ของทุกคน