ความแตกต่างในการแกะกล่องอัตโนมัติระหว่าง Java 6 กับ Java 7


107

ฉันสังเกตเห็นความแตกต่างของพฤติกรรมการแกะกล่องอัตโนมัติระหว่าง Java SE 6 และ Java SE 7 ฉันสงสัยว่าทำไมถึงเป็นเช่นนั้นเพราะฉันไม่พบเอกสารการเปลี่ยนแปลงใด ๆ ในพฤติกรรมนี้ระหว่างสองเวอร์ชันนี้

นี่คือตัวอย่างง่ายๆ:

Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

สิ่งนี้รวบรวมได้ดีกับ javac จาก Java SE 7 อย่างไรก็ตามหากฉันให้อาร์กิวเมนต์ "-source 1.6" แก่คอมไพเลอร์ฉันได้รับข้อผิดพลาดในบรรทัดสุดท้าย:

inconvertible types
found   : java.lang.Object
required: int

ฉันลองดาวน์โหลด Java SE 6 เพื่อคอมไพล์กับคอมไพเลอร์เวอร์ชัน 6 ดั้งเดิม (ไม่มีตัวเลือก -source ใด ๆ ) ตกลงและให้ข้อผิดพลาดเช่นเดียวกับด้านบน

ให้อะไร? จากการทดลองเพิ่มเติมดูเหมือนว่าการ unboxing ใน Java 6 จะสามารถ unbox ได้เฉพาะค่าที่ชัดเจน (ในเวลาคอมไพล์) เท่านั้นที่เป็นชนิดบรรจุกล่อง ตัวอย่างเช่นสิ่งนี้ใช้ได้ทั้งสองเวอร์ชัน:

Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

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

เพียงแค่อยากรู้: เนื่องจากการเปลี่ยนแปลงนี้เป็นไปได้ที่จะทำให้เกิดการแกะกล่อง "ผิด":

Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];

สิ่งนี้รวบรวมได้ดี แต่ให้ ClassCastException ที่รันไทม์

การอ้างอิงใด ๆ เกี่ยวกับเรื่องนี้?


17
น่าสนใจ. ส่วนผสมใหม่สำหรับความยุ่งเหยิงอัตโนมัติ ฉันคิดว่าตัวอย่างของคุณอาจจะง่ายและชัดเจนกว่าด้วยออบเจ็กต์เดียวแทนที่จะเป็นอาร์เรย์ Integer obj = new Integer(2); int x = (int)obj;: ทำงานบน Java 7 ให้ข้อผิดพลาดบน Java 6
leonbloy

1
คุณใช้ JDK ตัวไหน นอกจากนี้ยังอาจเกี่ยวข้องกับผู้ขายที่แตกต่างกัน ...
barfuin

1
@leonbloy: จุดดีเกี่ยวกับการทำให้เข้าใจง่ายฉันทำให้มันง่ายขึ้นบ้าง (จากรหัสเดิมของฉัน) แต่ก็หยุดเร็วเกินไป!
Morty

@ โทมัส: เป็น JDK ล่าสุด (สำหรับแต่ละเวอร์ชัน) จาก Oracle ที่ฉันใช้
Morty

2
อีกเหตุผลหนึ่งที่ไม่ใช้ autoboxing
gyorgyabraham

คำตอบ:


92

ดูเหมือนว่าภาษาในส่วน 5.5 Casting Conversion ของ Java 7 JLSได้รับการอัปเดตเมื่อเทียบกับส่วนเดียวกันใน Java 5/6 JLSซึ่งอาจเป็นการชี้แจงการแปลงที่อนุญาต

Java 7 JLS กล่าว

นิพจน์ของประเภทการอ้างอิงอาจผ่านการแคสต์การแปลงเป็นประเภทดั้งเดิมโดยไม่มีข้อผิดพลาดโดยการยกเลิกการแปลงกล่อง

Java 5/6:

ค่าของประเภทการอ้างอิงสามารถถูกโยนเป็นประเภทดั้งเดิมได้โดยการยกเลิกการแปลงกล่อง (§5.1.8)

Java 7 JLS ยังมีตาราง (ตารางที่ 5.1) ของการแปลงที่อนุญาต (ตารางนี้ไม่รวมอยู่ใน Java 5/6 JLS) ตั้งแต่ประเภทการอ้างอิงไปจนถึงแบบดั้งเดิม สิ่งนี้แสดงรายการ casts จาก Object ถึง primitives อย่างชัดเจนเป็นการแปลงอ้างอิงที่แคบลงด้วยการ unboxing

มีการอธิบายเหตุผลในอีเมลนี้ :

บรรทัดล่าง: หากข้อมูลจำเพาะ อนุญาตให้ (Object) (int) ต้องอนุญาต (int) (Object) ด้วย


35

คุณพูดถูก; เพื่อให้ง่ายขึ้น:

Object o = new Integer(1234);
int x = (int) o;

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

Java autoboxing มีกับดักและเซอร์ไพรส์มากขึ้น ตัวอย่างเช่น

Object obj = new Integer(1234);
long x = (long)obj;

จะคอมไพล์ แต่ล้มเหลว (ด้วยClassCastException) ที่รันไทม์ สิ่งนี้จะใช้งานได้แทน:

long x = (long)(int)obj;


2
ขอบคุณสำหรับคำตอบ. อย่างไรก็ตามมีสิ่งหนึ่งที่ฉันไม่เข้าใจ นี่คือคำชี้แจงของ JLS และการใช้งานที่มาพร้อมกัน (อ้างอิงการสนทนาทางไปรษณีย์) แต่เหตุใดจึงต้องทำเพื่อรองรับภาษาที่พิมพ์อื่น ๆ ใน JVM ท้ายที่สุดมันเป็นการเปลี่ยนภาษาไม่ใช่ VM: พฤติกรรมการแคสต์ของ VM ทำงานได้เหมือนเดิมคอมไพเลอร์จะใช้คุณสมบัตินี้โดยใช้กลไกที่มีอยู่สำหรับการแคสต์ไปยัง Integer และเรียก. intValue () ดังนั้นการเปลี่ยนแปลงนี้ในภาษา Java จะเหมาะสมได้อย่างไรช่วยรันภาษาอื่น ๆ บน VM ได้อย่างไร ฉันเห็นด้วยที่ลิงค์ของคุณแนะนำสิ่งนี้เพียงแค่สงสัย
Morty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.