โค้ด Java 8 สามารถคอมไพล์ให้รันบน Java 7 JVM ได้หรือไม่?


163

Java 8 แนะนำคุณสมบัติภาษาใหม่ที่สำคัญเช่นการแสดงออกแลมบ์ดา

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


คำตอบ:


146

ไม่การใช้คุณสมบัติ 1.8 ในซอร์สโค้ดของคุณต้องการให้คุณกำหนดเป้าหมาย 1.8 VM ฉันเพิ่งลอง Java 8 รีลีสใหม่และลองคอมไพล์ด้วย-target 1.7 -source 1.8และคอมไพเลอร์ปฏิเสธ:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8

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

ทำไม? "ใช่" หมายความว่า Java 8 สามารถรวบรวมเพื่อรันบน Java 7 VM ซึ่งไม่ถูกต้องตามคอมไพเลอร์ Java 8
JesperE

5
ตอนนี้ฉันเห็น: "ไม่" ของคุณตอบพาดหัวของคำถามไม่ใช่เนื้อหาของคำถาม
Abdull

58

วิธีการเริ่มต้นต้องมีการเปลี่ยนแปลงเช่น bytecode และ JVM ที่พวกเขาจะเป็นไปไม่ได้ที่จะทำบน Java 7 ตัวตรวจสอบ bytecode ของ Java 7 และด้านล่างจะปฏิเสธการเชื่อมต่อกับวิธีร่างกาย (ยกเว้นวิธีการเริ่มต้นคงที่) พยายามเลียนแบบวิธีการเริ่มต้นด้วยวิธีการแบบคงที่ทางด้านผู้โทรจะไม่ให้ผลลัพธ์เดียวกันเพราะวิธีการเริ่มต้นสามารถถูกแทนที่ในชั้นเรียนย่อย Retrolambdaมีการสนับสนุนที่ จำกัด สำหรับวิธีการเริ่มต้นย้อนกลับ แต่มันไม่สามารถย้อนกลับได้อย่างสมบูรณ์เพราะต้องใช้คุณสมบัติ JVM ใหม่อย่างแท้จริง

แลมบ์ดาสามารถเรียกใช้บน Java 7 ตามสภาพถ้าคลาส API ที่จำเป็นมีอยู่จริง คำสั่ง invokedynamic มีอยู่บน Java 7 แต่มันเป็นไปได้ที่จะนำ lambdas มาใช้เพื่อสร้างคลาส lambda ในเวลาคอมไพล์ (JDK 8 รุ่นแรกสร้างแบบนั้น) ในกรณีนี้มันจะทำงานบน Java เวอร์ชันใดก็ได้ (Oracle ตัดสินใจใช้ invokedynamic สำหรับ lambdas สำหรับการพิสูจน์ในอนาคตบางทีวันหนึ่ง JVM จะมีฟังก์ชั่นชั้นหนึ่งดังนั้น invokedynamic สามารถเปลี่ยนเป็นใช้แทนการสร้างคลาสสำหรับ lambda ทุกครั้งซึ่งจะช่วยปรับปรุงประสิทธิภาพ) สิ่งที่ Retrolambda ทำคือ มันประมวลผลคำสั่งที่เรียกใช้แบบซิงโครนัสทั้งหมดและแทนที่ด้วยคลาสแบบไม่ระบุชื่อ เช่นเดียวกับสิ่งที่ Java 8 ทำที่รันไทม์เมื่อ lamdba invokedynamic เรียกว่าเป็นครั้งแรก

คำอธิบายประกอบซ้ำเป็นเพียงน้ำตาลประโยค พวกเขาเป็น bytecode เข้ากันได้กับรุ่นก่อนหน้า ใน Java 7 คุณจะต้องใช้เมธอดตัวช่วย (เช่นgetAnnotationsByType ) ซึ่งจะซ่อนรายละเอียดการนำไปปฏิบัติของคำอธิบายประกอบคอนเทนเนอร์ซึ่งมีคำอธิบายประกอบซ้ำ ๆ

AFAIK, คำอธิบายประกอบประเภทมีอยู่ในเวลารวบรวมเท่านั้นดังนั้นจึงไม่จำเป็นต้องมีการเปลี่ยนแปลง bytecode ดังนั้นเพียงแค่เปลี่ยนหมายเลขเวอร์ชัน bytecode ของคลาสที่คอมไพล์ Java 8 ควรเพียงพอที่จะทำให้มันทำงานบน Java 7

ชื่อพารามิเตอร์เมธอดมีอยู่ใน bytecode ด้วย Java 7 ดังนั้นจึงเข้ากันได้ คุณสามารถเข้าถึงพวกเขาได้โดยการอ่าน bytecode ของวิธีการและดูชื่อตัวแปรท้องถิ่นในข้อมูลการแก้ปัญหาของวิธีการ ตัวอย่างเช่น Spring Framework ทำเช่นนั้นเพื่อนำ@PathVariableมาใช้ดังนั้นอาจมีวิธีการไลบรารีที่คุณสามารถโทรหาได้ เนื่องจากวิธีการอินเทอร์เฟซแบบนามธรรมไม่มีเนื้อความของวิธีการข้อมูลการตรวจแก้จุดบกพร่องนั้นไม่มีอยู่สำหรับวิธีการอินเทอร์เฟซใน Java 7 และ AFAIK ทั้งบน Java 8

คุณสมบัติใหม่อื่น ๆส่วนใหญ่เป็น API ใหม่การปรับปรุง HotSpot และเครื่องมือ APIs ใหม่บางส่วนมีอยู่ในห้องสมุดบุคคลที่สาม (เช่นThreeTen-Backportและstreamsupport )

ในที่สุดสรุป, วิธีการเริ่มต้นต้องมีคุณสมบัติ JVM ใหม่ แต่คุณสมบัติภาษาอื่นไม่ได้ หากคุณต้องการใช้พวกเขาคุณจะต้องรวบรวมรหัสใน Java 8 แล้วแปลง bytecode ด้วยRetrolambdaเป็นรูปแบบ Java 5/6/7 อย่างน้อยรุ่นไบต์จะต้องมีการเปลี่ยนแปลงและ javac ไม่อนุญาต-source 1.8 -target 1.7ดังนั้นจึงจำเป็นต้องมี retrotranslator


3
การพิมพ์คำอธิบายประกอบจริงสามารถมองเห็นได้ในขณะทำงาน stackoverflow.com/questions/22374612/…
พลวง

33

เท่าที่ฉันรู้ว่าการเปลี่ยนแปลงเหล่านี้ใน JDK 8 ไม่จำเป็นต้องมีการเพิ่มไบต์ใหม่ กำลังใช้เครื่องมือแลมบ์ดาอยู่ส่วนหนึ่งinvokeDynamic(ซึ่งมีอยู่แล้วใน JDK 7) ดังนั้นจากจุดยืนชุดคำสั่ง JVM ไม่ควรมีสิ่งใดที่ทำให้โค้ดเบสเข้ากันไม่ได้ อย่างไรก็ตามมีการเชื่อมโยง API และการปรับปรุงคอมไพเลอร์จำนวนมากซึ่งอาจทำให้โค้ดจาก JDK 8 ยากต่อการคอมไพล์ / รันภายใต้ JDK ก่อนหน้า (แต่ฉันยังไม่ได้ลอง)

บางทีเอกสารอ้างอิงต่อไปนี้สามารถช่วยเสริมสร้างความเข้าใจว่าการเปลี่ยนแปลงที่เกี่ยวข้องกับแลมบ์ดานั้นเป็นอย่างไร

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


7
ไม่มีไบต์ใหม่ แต่เป็นโครงสร้างใหม่ ผู้ตรวจสอบจะอ้วก
โจนาธานเอสฟิชเชอร์

12
ตัวอย่างที่ดีคืออินเทอร์เฟซ ตอนนี้พวกเขาสามารถมีวิธีการ ตัวตรวจสอบ Java7 ไม่ได้ติดตั้งเพื่อจัดการสิ่งนี้ ไบต์เก่าทั้งหมดถูกนำมาใช้ แต่เป็นวิธีใหม่
Jonathan S. Fisher

1
ฉันสงสัยว่าคอมไพเลอร์สกาล่าสามารถมีคุณสมบัติภาษาจำนวนมากบรรลุเป้าหมายการเปิดตัว jvm ของแม้กระทั่ง jdk5 ได้อย่างไร
Marinos

1
@MarinosAn คุณหมายถึงอะไรกันแน่? MI มีลักษณะที่มีวิธีการที่เป็นรูปธรรมเช่นclass C extends A with Bถูกนำมาใช้กับการเชื่อมต่อปกติAและBและการเรียนสหายและA$class B$classclass Cเพียงแค่ส่งต่อเมธอดไปยังคลาสสหายแบบคงที่ ประเภทตัวเองไม่ได้บังคับใช้ทั้งหมด lambdas ถูก transpiled ในเวลารวบรวมเป็นชั้นในนามธรรมดังนั้นการnew D with A with Bแสดงออก การจับคู่รูปแบบเป็นพวงของโครงสร้าง if-else ผลตอบแทนที่ไม่ใช่ท้องถิ่น? ลองจับกลไกจากแลมบ์ดา มีอะไรเหลือ (ที่น่าสนใจ scalac ของฉันบอกว่า 1.6 เป็นค่าเริ่มต้น)
Adowrath

1
แน่นอนประเภทตนเอง ฯลฯ ถูกเข้ารหัสในคุณลักษณะคลาสพิเศษและคำอธิบายประกอบเพื่อให้ scalac สามารถใช้และบังคับใช้กฎเมื่อใช้คลาสที่คอมไพล์แล้ว
Adowrath


-5

คุณสามารถทำได้-source 1.7 -target 1.7แล้วมันจะรวบรวม แต่มันจะไม่รวบรวมถ้าคุณมีคุณสมบัติเฉพาะของ java 8 เช่น lambdas


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