Questa è una domanda molto semplice con una risposta molto complessa. Per favore, sopportami mentre cerco di spiegarlo.
Guardando il codice sorgente in cui l'eccezione è sollevata in Class
(non sono sicuro perché il vostro stack trace non dà i numeri di riga in Class
):
try
{
Class[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// removed some code that was not relevant
}
catch (NoSuchMethodException e)
{
throw new InstantiationException(getName());
}
si vede che è NoSuchMethodException
essere retharrown come InstantiationException
. Ciò significa che non esiste un costruttore no-arg per il tipo di classe di object2
.
Innanzitutto, che tipo è object2
? Con il codice
System.out.println("object2 class: " + object2.getClass());
vediamo che
oggetto2 classe: class junk.NewMain $ 1
che è corretto (ho eseguito il codice di esempio nel pacchetto spazzatura, classe NewMain).
Quali sono quindi i costruttori di junk.NewMain$1
?
Class obj2Class = object2.getClass();
try
{
Constructor[] ctors = obj2Class.getDeclaredConstructors();
for (Constructor cc : ctors)
{
System.out.println("my ctor is " + cc.toString());
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
che ci dà
mia ctor è junk.NewMain $ 1 (java.util.Calendar)
Così la vostra classe anonima è alla ricerca di un Calendar
da passare in. Questo funzionerà quindi per voi:
Object newObj = ctors[0].newInstance(Calendar.getInstance());
Se avete qualcosa come e questo:
final String finalString = "I'm final :)";
final Integer finalInteger = new Integer(30);
final Calendar calendar = Calendar.getInstance();
Object object2 = new Object()
{
{
System.out.println("Instance initializing block");
System.out.println(finalString);
System.out.println("My integer is " + finalInteger);
System.out.println(calendar.getTime().toString());
}
private void hiddenMethod()
{
System.out.println("Use reflection to find me :)");
}
};
allora la mia chiamata a newInstance
non funziona perché non ci sono abbastanza argomenti nella ctor, perché ora vuole:
mia ctor è junk.NewMain $ 1 (java. lang.Integer, java.util.Calendar)
Se dunque io un'istanza di quello con
Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance());
una sbirciatina all'interno utilizzando il debugger mostra che finalInteger
è 25 e non il valore finale 30.
Le cose sono leggermente complicate perché stai facendo tutto quanto sopra in un contesto statico. Se si prende tutto il codice sopra e spostarlo in un metodo non statico in questo modo (si ricordi, la mia classe è junk.NewMain):
public static void main(String[] args)
{
NewMain nm = new NewMain();
nm.doIt();
}
public void doIt()
{
final String finalString = "I'm final :)";
// etc etc
}
troverete il ctor per la classe interna è ora (rimozione la mia aggiunta di riferimento Integer):
mia ctor è junk.NewMain $ 1 (junk.NewMain, java.util.Calendar)
La Java Language Specification, sezione 15.9.3 spiega in questo modo:
Se C è una classe anonima, e superclasse diretta di C, S, è un classe interna, quindi:
- Se S è una classe locale e S avviene in un contesto statico, allora gli argomenti nell'elenco di argomenti, se presenti, sono gli argomenti a il costruttore, nell'ordine in cui appaiono nell'espressione.
- caso contrario, l'istanza immediatamente racchiude dei rispetto al S è il primo argomento del costruttore, seguito dagli argomenti nella lista degli argomenti dell'espressione creazione dell'istanza classe eventualmente, nell'ordine in cui appaiono in l'espressione.
Perché il costruttore anonimo accetta argomenti?
Poiché non è possibile creare un costruttore per una classe interna anonima, il blocco di inizializzazione dell'istanza serve a tale scopo (ricordare, si dispone di una sola istanza di tale classe interna anonima). La VM non ha conoscenza della classe interna in quanto il compilatore separa tutto come singole classi (ad es. Junk.NewMain $ 1). Il ctor per quella classe contiene il contenuto del programma di inizializzazione dell'istanza.
Questo è explayed da JLS 15.9.5.1 Anonymous Constructors:
... il costruttore Anonymous ha un parametro formale per ciascun argomento reale per l'espressione creazione istanza di classe in cui C è dichiarata.
L'inizializzatore di istanza ha un riferimento a un oggetto Calendar
. In quale altro modo il compilatore otterrà quel valore di runtime nella tua classe interna (che viene creata come una semplice classe per la VM) tranne attraverso il costruttore?
Infine (yay), la risposta all'ultima domanda bruciata. Perché il costruttore non richiede un String
? L'ultimo pezzo di JLS 3.10.5 spiega che:
Strings calcolati da espressioni costanti sono calcolati al momento della compilazione e poi trattati come se fossero letterali.
In altre parole, il valore String
è noto al momento della compilazione, perché è un letterale in modo che non ha bisogno di essere parte del costruttore anonimo. Per dimostrare questo è il caso ci prova l'istruzione successiva in JLS 3.10.5:
Strings calcolate dalla concatenazione in fase di esecuzione sono state recentemente create e pertanto distinta.
di modificare il codice nel seguente modo:
String str1 = "I'm";
String str2 = " final!";
final String finalString = str1 + str2
e troverete il vostro ctor è ora (nel contesto non statica):
mia ctor è junk.NewMain $ 1 (junk.NewMain, java.lang.String, java.util.Calendar)
Phew. Spero che questo abbia senso ed è stato utile. Ho imparato molto, questo è sicuro!
E se l'instnace di Calendar non fosse definitiva? – SJuan76
@ SJuan76. ... allora non saresti stato in grado di trasmetterlo a una classe interiore anonima. –