È possibile compilare il codice Java 8 per l'esecuzione su JVM Java 7?


163

Java 8 introduce importanti nuove funzionalità linguistiche come le espressioni lambda.

Questi cambiamenti nella lingua sono accompagnati da cambiamenti così significativi nel bytecode compilato che ne impedirebbero l'esecuzione su una macchina virtuale Java 7 senza usare un retrotranslator?


Risposte:


146

No, l'utilizzo delle funzionalità 1.8 nel codice sorgente richiede di scegliere come target una VM 1.8. Ho appena provato la nuova versione di Java 8 e ho provato a compilare -target 1.7 -source 1.8, e il compilatore rifiuta:

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

4
No, non credo che lo farà. Java ha una piccola parte del mercato desktop, ma mantiene quella piccola quota in una presa abbastanza stretta. Ma ostacola l'adozione di nuove versioni e funzionalità. Non sarò in grado di utilizzare le funzionalità Java 8 nel codice che scrivo da un po 'di tempo, poiché voglio evitare che le persone debbano aggiornare la loro installazione Java locale.
JesperE,

Perché? "Sì" implica che Java 8 può essere compilato per essere eseguito su una VM Java 7, che non è corretto secondo il compilatore Java 8.
JesperE,

5
Ora vedo: il tuo "No" risponde al titolo della domanda, non al corpo della domanda.
Abdull,

58

I metodi predefiniti richiedono tali modifiche al bytecode e alla JVM che sarebbero stati impossibili da eseguire su Java 7. Il verificatore bytecode di Java 7 e versioni precedenti rifiuterà le interfacce con i corpi del metodo (ad eccezione del metodo di inizializzazione statica). Cercare di emulare metodi predefiniti con metodi statici sul lato chiamante non produrrebbe gli stessi risultati, poiché i metodi predefiniti possono essere sovrascritti in sottoclassi. Retrolambda ha un supporto limitato per i metodi predefiniti di backport, ma non può mai essere completamente backport perché richiede veramente nuove funzionalità JVM.

Lambdas potrebbe funzionare su Java 7 così com'è, se le classi API necessarie esistessero proprio lì. L'istruzione invokedynamic esiste su Java 7, ma sarebbe stato possibile implementare lambdas in modo da generare le classi lambda in fase di compilazione (le prime build JDK 8 lo facevano in quel modo) nel qual caso avrebbe funzionato su qualsiasi versione Java. (Oracle ha deciso di utilizzare invokedynamic per lambdas per future prove; forse un giorno JVM avrà funzioni di prima classe, quindi quindi invokedynamic può essere modificato per usarle invece di generare una classe per ogni lambda, migliorando così le prestazioni.) Retrolambda fa che elabora tutte quelle istruzioni invocate e le sostituisce con classi anonime; lo stesso di quello che fa Java 8 in fase di esecuzione quando una chiamata invocata dinamica lamdba viene chiamata per la prima volta.

La ripetizione delle annotazioni è solo zucchero sintattico. Sono compatibili bytecode con le versioni precedenti. In Java 7 dovresti solo implementare te stesso i metodi di supporto (ad esempio getAnnotationsByType ) che nascondono i dettagli di implementazione di un'annotazione contenitore che contiene le annotazioni ripetute.

AFAIK, le Annotazioni di tipo esistono solo al momento della compilazione, quindi non dovrebbero richiedere modifiche al bytecode, quindi basta cambiare il numero di versione del bytecode delle classi compilate Java 8 per farle funzionare su Java 7.

I nomi dei parametri del metodo esistono nel bytecode con Java 7, quindi anche questo è compatibile. È possibile accedervi leggendo il bytecode del metodo e osservando i nomi delle variabili locali nelle informazioni di debug del metodo. Ad esempio Spring Framework fa esattamente questo per implementare @PathVariable , quindi esiste probabilmente un metodo di libreria che potresti chiamare. Poiché i metodi di interfaccia astratti non hanno un corpo di metodo, tali informazioni di debug non esistono per i metodi di interfaccia in Java 7 e AFAIK né su Java 8.

Le altre nuove funzionalità sono principalmente nuove API, miglioramenti a HotSpot e strumenti. Alcune delle nuove API sono disponibili come librerie di terze parti (ad esempio ThreeTen-Backport e streamsupport ).

Summa summarum, i metodi predefiniti richiedono nuove funzionalità JVM ma le altre funzionalità linguistiche no. Se si desidera utilizzarli, è necessario compilare il codice in Java 8 e quindi trasformare il bytecode con Retrolambda in formato Java 5/6/7. Almeno la versione del bytecode deve essere cambiata e javac non consente -source 1.8 -target 1.7quindi un retrotranslator.


3
In realtà le annotazioni dei tipi possono essere visibili in fase di esecuzione. stackoverflow.com/questions/22374612/…
Antimonio

33

Per quanto ne so, nessuna di queste modifiche in JDK 8 ha richiesto l'aggiunta di nuovi bytecode. Parte della strumentazione lambda viene eseguita utilizzando invokeDynamic(che esiste già in JDK 7). Quindi, dal punto di vista del set di istruzioni JVM, nulla dovrebbe rendere incompatibile la base di codice. Ci sono, tuttavia, molti miglioramenti associati all'API e al compilatore che potrebbero rendere difficile compilare / eseguire il codice da JDK 8 con JDK precedenti (ma non l'ho provato).

Forse il seguente materiale di riferimento può aiutare in qualche modo ad arricchire la comprensione di come vengono strumentalizzati i cambiamenti relativi alla lambda.

Questi spiegano in dettaglio come le cose sono strumentate sotto il cofano. Forse puoi trovare la risposta alle tue domande lì.


7
Nessun nuovo bytecode, ma nuove strutture. Il verificatore vomiterà.
Jonathan S. Fisher,

12
Un buon esempio sono le interfacce. Ora possono contenere metodi. Il verificatore Java7 non è attrezzato per gestire questo. Vengono utilizzati tutti i vecchi bytecode, ma in un modo nuovo.
Jonathan S. Fisher,

1
Mi chiedo come può il compilatore scala con così tante funzioni linguistiche ottenere una versione jvm target anche di jdk5.
Marinos Il

1
@MarinosAn Cosa intendi esattamente? L'IM con tratti che contengono metodi concreti, ad esempio class C extends A with B, è implementata con interfacce normali Ae Be classi associate A$classe B$class. class Csemplicemente inoltra i metodi alle classi companion statiche. I tipi di sé non vengono affatto applicati, le lambda vengono traspilate in fase di compilazione in astratte classi interne, così è new D with A with Bun'espressione. Il pattern matching è un gruppo di strutture if-else. Ritorni non locali? Meccanismo try-catch dalla lambda. Qualcosa è rimasto? (È interessante notare che il mio scalac dice che 1.6 è l'impostazione predefinita)
Adowrath

1
Naturalmente, i tipi di sé ecc. Sono codificati in attributi e annotazioni di classe speciali in modo che scalac possa usare e applicare le regole quando si usano classi già compilate.
Adowrath,


-5

Puoi fare -source 1.7 -target 1.7quindi compilerà. Ma non verrà compilato se si dispone di funzionalità specifiche di java 8 come lambdas


La domanda pone esplicitamente l'uso delle nuove funzionalità linguistiche, quindi -source 1.7non volerà.
Toolforger,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.