Sovraccarico del metodo e scelta del tipo più specifico

Il codice di esempio è:

public class OverloadingTest { public static void test(Object obj){ System.out.println("Object called"); } public static void test(String obj){ System.out.println("String called"); } public static void main(String[] args){ test(null); System.out.println("10%2==0 is "+(10%2==0)); test((10%2==0)?null:new Object()); test((10%2==0)?null:null); } 

E l’output è:

String chiamato
10% 2 == 0 è vero
Oggetto chiamato
String chiamato

La prima chiamata a test(null) richiama il metodo con argomento String , che è comprensibile in base a The Java Language Specification .

1) Qualcuno può spiegarmi su quale base test() è invocato nelle chiamate precedenti?

2) Di nuovo quando mettiamo, diciamo una condizione if :

  if(10%2==0){ test(null); } else { test(new Object()); } 

Invoca sempre il metodo con argomento String .

Il compilatore calcolerà l’espressione (10%2) durante la compilazione? Voglio sapere se le espressioni sono calcolate in fase di compilazione o di esecuzione. Grazie.

Java utilizza l’associazione anticipata. Il metodo più specifico è scelto al momento della compilazione. Il metodo più specifico è scelto in base al numero di parametri e al tipo di parametri. Il numero di parametri non è rilevante in questo caso. Questo ci lascia con il tipo di parametri.

Che tipo hanno i parametri? Entrambi i parametri sono espressioni, utilizzando l’operatore condizionale ternario. La domanda si riduce a: Che tipo restituisce l’operatore ternario condizionale? Il tipo è calcolato al momento della compilazione.

Date sono le due espressioni:

 (10%2==0)? null : new Object(); // A (10%2==0)? null : null; // B 

Le regole di valutazione del tipo sono elencate qui . In B è facile, entrambi i termini sono esattamente gli stessi: verrà restituito null ( qualunque sia il tipo ) (JLS: “Se il secondo e il terzo operando hanno lo stesso tipo (che può essere il tipo null), allora quello è il tipo di espressione condizionale. “). In A il secondo termine è di una class specifica. Poiché questo è più specifico e null può essere sostituito per un object di class Object il tipo dell’intera espressione è Object (JLS: “Se uno dei secondi e terzi operandi è di tipo nullo e il tipo dell’altro è un riferimento digita, quindi il tipo di espressione condizionale è quel tipo di riferimento. “).

Dopo la valutazione del tipo delle espressioni, la selezione del metodo è come previsto.

L’esempio con if si dà è diverso: si chiamano i metodi con oggetti di due tipi diversi . L’operatore condizionale ternario viene sempre valutato su un tipo in fase di compilazione che si adatta a entrambi i termini.

 test((10%2==0)?null:new Object()); 

Equivale a:

 Object o; if(10%2==0) o=null; else o=new Object(); test(o); 

Poiché il tipo di o è Object (proprio come il tipo di (10%2==0)?null:new Object() ) test(Object) sarà sempre chiamato. Il valore di o non ha importanza.

JLS 15.25:

Il tipo di un’espressione condizionale è determinato come segue:

[…]

  • Se uno tra il secondo e il terzo operando è di tipo nullo e il tipo dell’altro è un tipo di riferimento, il tipo di espressione condizionale è quel tipo di riferimento.

[…]

Quindi il tipo di

 10 % 2 == 0 ? null : new Object(); 

è un object.

La tua risposta è: Runtime perché in runtime specificare parametro è un’istanza di String o meno così in fase di compilazione non può trovarlo.

Questa è davvero una bella domanda.

Lasciatemi provare a chiarire il tuo codice che hai scritto sopra.

  • Nella tua prima chiamata al metodo

Test (null);

In questo il null sarà convertito in string type quindi chiamando il test(String obj) , come da JLS sei convinto della chiamata.

  • Nella seconda chiamata di metodo

test ((10% 2 == 0)? null: new Object ());

Che restituirà il valore “vero” booleano. Quindi, il primo valore “vero” booleano esegue il cast automatico in object class Wrapper booleano. Boolean wrapper Object sta trovando la migliore corrispondenza con la tua new Object() opzione new Object() nell’operatore ternario. E il metodo chiama Object come parametro, quindi chiama il metodo seguente

test statico pubblico vuoto (object obj)

Per il bene dell’esperimento puoi provare le seguenti combinazioni, quindi otterrai una maggiore chiarezza.

test ((10% 2 == 0)? new Object (): “stringObj”);

test ((10% 2 == 0)? new Object (): null);

test ((10% 2 == 0)? “stringObj”: null);

  • Finalmente nell’ultimo quando chiami con il seguente codice.

Test (? (10% 2 == 0) nullo: nullo);

Questa volta ritorna come valore “vero” booleano e seguirà di nuovo gli stessi cast come spiegato sopra. Ma questa volta non c’è nessun new Object() parametro new Object() presente nel tuo operatore ternario. Quindi verrà eseguito il cast automatico in Object null . Di nuovo segue la stessa chiamata di metodo come la prima chiamata al metodo.

  • Nell’ultimo quando hai chiesto il codice se inserissi if .. else statement. Poi anche il compilatore che prende la giusta decisione con il codice.

if (10% 2 == 0) {test (null); }

Qui tutte le volte che la condizione if è vera e chiama questo test(null) codice test(null) . Pertanto, per tutto il tempo chiama il primo metodo test(String obj) con String come parametro come spiegato sopra.

Penso che il tuo problema sia che stai prendendo l’assunto sbagliato, le tue espressioni:

 test((10%2==0)?null:new Object()); 

e

 test((10%2==0)?null:null; 

Chiamerà sempre test (null), ed è per questo che passeranno attraverso test (Object).

come @Banthar menziona l’operatore ?: assegna un valore a una variabile, quindi valuta la condizione. D’altra parte, la condizione if che hai citato è sempre vera, quindi il compilatore sostituirà l’intero blocco if-else con il solo corpo del if .

1) il metodo test() è determinato dal tipo di parametro al momento della compilazione:

 test((Object) null); test((Object)"String"); 

produzione :

 Object called Object called 

2) Il compilatore è ancora più intelligente, il codice compilato è equivalente a solo:

 test(null); 

puoi controllare il bytecode con javap -c :

  0: aconst_null 1: invokestatic #6 // Method test:(Ljava/lang/String;)V 4: return 

Questo è ciò che le specifiche della lingua Java dicono sul problema.

Se più di una dichiarazione di metodo è accessibile e applicabile a una chiamata di metodo, è necessario sceglierne una per fornire il descrittore per la spedizione del metodo di runtime. Il linguaggio di programmazione Java utilizza la regola per scegliere il metodo più specifico.

Questo è un metodo di prova (stringa) nel tuo caso.

E per questo se aggiungi …

 public static void test(Integer obj){ System.out.println("Ingeter called"); } 

mostrerà l’errore di compilazione -Il metodo test (String) è ambiguo per il tipo OverloadingTest.

Proprio come dice JLS:

È ansible che nessun metodo sia il più specifico, perché ci sono due o più metodi massimamente specifici. In questo caso:

Se tutti i metodi massimamente specifici hanno la stessa firma, allora: Se uno dei metodi massimamente specifici non è dichiarato astratto, è il metodo più specifico. Altrimenti, tutti i metodi massimamente specifici sono necessariamente dichiarati astratti. Il metodo più specifico è scelto arbitrariamente tra i metodi massimamente specifici. Tuttavia, si considera che il metodo più specifico lanci un’eccezione controllata se e solo se tale eccezione è dichiarata nelle clausole di tiro di ciascuno dei metodi massimamente specifici. Altrimenti, diciamo che l’invocazione del metodo è ambigua e si verifica un errore in fase di compilazione.