สามารถคอมไพล์โค้ด Java 8 เพื่อทำงานบน Java 7 JVM ได้หรือไม่

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

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


person Nicola Ambrosetti    schedule 22.04.2013    source แหล่งที่มา
comment
ความเป็นไปได้ที่ซ้ำกันของ มีตัวอย่างเฉพาะของการย้อนกลับหรือไม่ ความเข้ากันไม่ได้ระหว่างเวอร์ชัน Java?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 04.05.2015


คำตอบ (5)


ไม่ การใช้คุณสมบัติ 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
person JesperE    schedule 18.03.2014
comment
ไม่ ฉันไม่คิดว่ามันจะเกิดขึ้น Java มีส่วนแบ่งเล็กน้อยในตลาดเดสก์ท็อป แต่ยังคงรักษาส่วนแบ่งเพียงเล็กน้อยนั้นไว้ได้ค่อนข้างแน่น แต่มันขัดขวางการนำเวอร์ชันและฟีเจอร์ใหม่ๆ มาใช้ ฉันจะไม่สามารถใช้คุณสมบัติ Java 8 ในโค้ดที่ฉันเขียนได้สักระยะหนึ่ง เนื่องจากฉันต้องการหลีกเลี่ยงไม่ให้ผู้อื่นต้องอัปเกรดการติดตั้ง Java ในเครื่องของตน - person JesperE; 15.08.2014
comment
ทำไม ใช่จะบ่งบอกว่า Java 8 สามารถคอมไพล์เพื่อทำงานบน Java 7 VM ซึ่งไม่ถูกต้องตามคอมไพเลอร์ Java 8 - person JesperE; 28.10.2014
comment
ตอนนี้ฉันเห็นแล้ว: Your No ตอบคำถามพาดหัว ไม่ใช่เนื้อหาของคำถาม - person Abdull; 28.10.2014

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

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

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

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

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

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

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

person Esko Luontola    schedule 20.05.2014
comment
จริงๆ แล้วคำอธิบายประกอบประเภทสามารถมองเห็นรันไทม์ได้ stackoverflow.com/questions/22374612/ - person Antimony; 19.02.2016

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

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

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

person Edwin Dalorzo    schedule 29.04.2013
comment
ไม่มีรหัสไบต์ใหม่ แต่มีโครงสร้างใหม่ ผู้ตรวจสอบจะอ้วก - person Jonathan S. Fisher; 12.06.2014
comment
@exabrial คุณหมายถึงอะไรกับโครงสร้าง? - person Abdull; 28.10.2014
comment
ตัวอย่างที่ดีคืออินเทอร์เฟซ ตอนนี้สามารถมีวิธีการได้แล้ว ตัวตรวจสอบ Java7 ไม่ได้ติดตั้งไว้เพื่อจัดการสิ่งนี้ มีการใช้ bytecodes เก่าทั้งหมด แต่ในรูปแบบใหม่ - person Jonathan S. Fisher; 29.10.2014
comment
ฉันสงสัยว่าคอมไพเลอร์สกาล่าที่มีคุณสมบัติภาษามากมายสามารถบรรลุเป้าหมาย jvm แม้กระทั่ง jdk5 ได้อย่างไร - person Marinos An; 05.04.2016
comment
@MarinosAn คุณหมายถึงอะไรกันแน่? MI ที่มีลักษณะที่มีวิธีการที่เป็นรูปธรรม เช่น class C extends A with B ถูกนำไปใช้กับอินเทอร์เฟซปกติ A และ B และคลาสที่แสดงร่วมกัน A$class และ B$class คลาส C เพียงส่งต่อวิธีการไปยังคลาสสหายแบบคงที่ ไม่มีการบังคับใช้ประเภทตนเองเลย lambdas จะถูกแปลงตามเวลาคอมไพล์เป็นคลาสภายในแบบนามธรรม ดังนั้นนิพจน์ new D with A with B ก็เช่นกัน การจับคู่รูปแบบเป็นกลุ่มของโครงสร้าง if-else การคืนสินค้านอกพื้นที่? กลไกลองจับจากแลมบ์ดา มีอะไรเหลือบ้างไหม? (ที่น่าสนใจ scalac ของฉันบอกว่า 1.6 เป็นค่าเริ่มต้น) - person Adowrath; 08.04.2017
comment
แน่นอนว่าประเภทของตัวเอง ฯลฯ จะถูกเข้ารหัสในแอตทริบิวต์คลาสพิเศษและคำอธิบายประกอบเพื่อให้ scalac สามารถใช้และบังคับใช้กฎเมื่อใช้คลาสที่คอมไพล์แล้ว - person Adowrath; 08.04.2017
comment
@Adowrath แล้ว Scala มีคุณสมบัติพร้อมการใช้งานเริ่มต้นอย่างไร ดูเหมือนวิธีการเริ่มต้นในอินเทอร์เฟซ Java 8 - person Franklin Yu; 06.09.2017
comment
@FranklinYu หากเป้าหมายคือ 1.8 หรือสูงกว่า: วิธีการเริ่มต้นใช่ 1.7 และต่ำกว่า? ถ้า trait A { def foo: Int = 12 } และ class C extends A คุณจะได้: interface A { int foo(); }, class A$class { public static int foo(A $this) { return 12; } } และ class C implements A { public int foo() { return A$class.foo(this); } } ดังนั้นการนำเมธอดเริ่มต้นไปใช้งานจะถูกย้ายไปยังคลาส X$class และในแต่ละคลาสของการนำไปใช้ของคุณลักษณะ คอมไพลเลอร์จะสร้างเมธอด stub ที่เพิ่งส่งต่อไปยังเมธอดใน X$class โดยให้ this เป็นอาร์กิวเมนต์ (เพราะถ้า X.foo ถูกเรียก X.bar) . - person Adowrath; 07.09.2017
comment
ดังนั้นโค้ดใน Java 7 ไม่สามารถเรียกการใช้งานเริ่มต้นของอินเทอร์เฟซ Scala เมื่อทำงานบน JRE 7 ได้ หากนั่นเป็นที่ยอมรับของผู้ใช้ Scala ทำไมเราไม่สามารถทำเช่นเดียวกันกับ Java 8 ได้? - person Franklin Yu; 07.09.2017
comment
@FranklinYu โอ้ขอโทษ ใช่ มันทำไม่ได้ JRE 7 ไม่สามารถโหลด Scala Traits ใหม่ด้วยวิธีเริ่มต้นได้ แต่ไม่สามารถโหลดอินเทอร์เฟซ Java 8 ด้วยวิธีเริ่มต้นได้ Scala 2.12 ขึ้นไปมีการพึ่งพา Java 8 อย่างชัดเจนไม่ใช่แค่ส่วนนี้เท่านั้น แต่ยังอยู่ในประเภท SAM ด้วย (เช่น java.util.Function และ scala.Function1 ต่างก็ได้รับการจัดการเหมือนกัน และ a => a + 1 สามารถคอมไพล์ได้ทั้งสองอย่าง) การคอมไพล์โดยใช้ java.lang.invoke.LambdaMetaFactory - person Adowrath; 08.10.2017
comment
@Adowrath ดังนั้น Scala จึงไม่บรรลุเป้าหมายการเปิดตัว jvm แม้แต่ jdk5 ใช่ไหม - person Franklin Yu; 09.10.2017
comment
@FranklinYu 2.12 และสูงกว่าไม่ได้อีกต่อไปไม่ (เว้นแต่ว่าจะสามารถรองรับ -target:jvm-1.7 ได้ แต่ฉันต้องลอง) 2.11 และต่ำกว่า? ใช่. (ถึงแม้จะคิดว่าเป็น Java 6 ขึ้นไปมานานแล้ว แต่ก็ไม่รู้ว่าจะแม่นหรือเปล่า) - person Adowrath; 09.10.2017

หากคุณยินดีที่จะใช้ "ตัวแปลย้อนยุค" ลองใช้ Retrolambda ที่ยอดเยี่ยมของ Esko Luontola: https://github.com/orfjackal/retrolambda

person Stefan Zobel    schedule 17.05.2014

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

person kalgecin    schedule 20.12.2014
comment
คำถามแสดงการใช้คุณลักษณะภาษาใหม่อย่างชัดเจน ดังนั้น -source 1.7 จะไม่บินไป - person toolforger; 28.01.2019