Come viene scelto un metodo sovraccarico quando un parametro è il valore null letterale?


98

Mi sono imbattuto in questa domanda in un quiz,

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

L'output di questo programma è "String Version". Ma non sono stato in grado di capire perché il passaggio di un valore nullo a un metodo sovraccarico ha scelto la versione stringa. Null è una variabile String che punta a nulla?

Tuttavia, quando il codice viene modificato in,

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

dà un errore di compilazione che dice "Il metodo del metodo (StringBuffer) è ambiguo per il tipo MoneyCalc"


È possibile assegnare una stringa a un valore null in modo che sia valida e l'ordine per java e la maggior parte dei linguaggi di programmazione sia adatto al tipo più vicino e quindi all'oggetto.
JonH


6
Apparentemente questo è stato chiuso come duplicato da persone che hanno letto solo il titolo. La vera domanda qui riguarda il motivo per cui è stato scelto un sovraccarico specifico, non "cosa è nullo".
Interjay

Risposte:


102

Null è una variabile String che punta a nulla?

Un riferimento null può essere convertito in un'espressione di qualsiasi tipo di classe. Quindi, nel caso di String, va bene:

String x = null;

L' Stringoverload qui viene scelto perché il compilatore Java sceglie l' overload più specifico , come da sezione 15.12.2.5 del JLS . In particolare:

L'intuizione informale è che un metodo è più specifico di un altro se qualsiasi invocazione gestita dal primo metodo può essere passata all'altro senza un errore di tipo in fase di compilazione.

Nel tuo secondo caso, entrambi i metodi sono ancora applicabili, ma né StringStringBufferè più specifico dell'altro, quindi nessuno dei due metodi è più specifico dell'altro, da qui l'errore del compilatore.


3
E in che modo l'overload di String è più specifico dell'overload di Object?
user1610015

12
Perché un Objectpuò prendere qualsiasi tipo e avvolgerlo in un Object, mentre a Stringpuò solo prendere un String. In questo caso, Stringè più specifico di un tipo rispetto al Objecttipo.
JonH

10
@zakSyed Se ti chiedessero cosa è "String" o "Object" più specializzato, cosa risponderesti? Evidentemente "String", giusto? Se ti chiedessero: cosa sono "String" o "StringBuffer" più specializzati? Non c'è risposta, sono entrambe specializzazioni ortogonali, come puoi scegliere tra loro? Quindi devi essere esplicito riguardo a quale vuoi (cioè, lanciando il tuo riferimento nullo question.method((String)null))
Edwin Dalorzo

1
@ JonSkeet: ha senso. Quindi fondamentalmente cerca un metodo in base alla regola più specifica e se non è in grado di decidere quale è più specifico, genererebbe un errore in fase di compilazione.
zakSyed

3
@JonH "null" è il tipo di riferimento, se uno dei metodi riceve un tipo primitivo come parametro (cioè int) non sarà nemmeno considerato dal compilatore quando sceglie il metodo giusto da invocare per un riferimento di tipo null. È fonte di confusione se con "Int" intendevi java.lang.Integer o se intendevi il tipo primitivo int.
Edwin Dalorzo

9

Inoltre, JLS 3.10.7 dichiara anche che "null" è un valore letterale di "tipo null". Quindi esiste un tipo chiamato "null".

Successivamente, JLS 4.1 afferma che esiste un tipo null di cui è impossibile dichiarare le variabili, ma è possibile utilizzarlo solo tramite il letterale null. Successivamente si dice:

Il riferimento null può sempre subire una conversione di riferimento di ampliamento in qualsiasi tipo di riferimento.

Perché il compilatore sceglie di estenderlo a String potrebbe essere spiegato nella risposta di Jon .


Sono abbastanza sicuro che quel tipo sia Void.
xavierm02

2
@ xavierm02 Non si fa menzione di questo nella specifica del linguaggio Java. Puoi citare il tuo riferimento in modo che tutti noi possiamo verificare la tua richiesta?
Edwin Dalorzo

Non ho mai letto quella cosa. Ma ho fatto un po 'di Java quest'estate e puoi sovraccaricare un metodo che prende un oggetto con un metodo che prende un oggetto Void.
xavierm02

È più una classe che un tipo.
xavierm02

4
@ xavierm02 Certo che puoi. Puoi sovraccaricare un metodo in Java con qualsiasi altro tipo tu voglia. Ciò, tuttavia, non ha nulla a che fare con la domanda o la mia risposta.
Edwin Dalorzo

2

È possibile assegnare a stringa un nullvalore in modo che sia valido e l'ordine per java e la maggior parte dei linguaggi di programmazione sia adatto al tipo più vicino e quindi all'oggetto.


2

Per rispondere alla domanda nel titolo: nullnon è né un Stringné un Object, ma può essere assegnato un riferimento a entrambi null.

In realtà sono sorpreso che questo codice venga compilato. Ho provato qualcosa di simile in precedenza e ho ricevuto un errore del compilatore che diceva che la chiamata era ambigua.

Tuttavia, in questo caso, sembra che il compilatore stia scegliendo il metodo più basso nella catena alimentare. Si presume che tu voglia la versione meno generica del metodo per aiutarti.

Dovrò vedere se riesco a scavare l'esempio in cui ho ricevuto un errore del compilatore in questo (apparentemente) identico scenario, però ...]

EDIT: Capisco. Nella versione che ho creato, avevo due metodi sovraccarichi che accettano un Stringe un Integer. In questo scenario, non esiste un parametro "più specifico" (come in Objecte String), quindi non può scegliere tra di essi, a differenza del codice.

Domanda molto interessante!


null non è né l'uno né l'altro ma può essere assegnato a entrambi, questa è la differenza e compilerà perché non dovrebbe - è un codice assolutamente valido.
JonH

0

Poiché il tipo String è più specifico del tipo Object. Supponiamo che tu aggiunga un altro metodo che accetta un tipo Integer.

public void method(Integer i) {
      System.out.println("Integer Version");
   }

Quindi riceverai un errore del compilatore che dice che la chiamata è ambigua. Come ora abbiamo due metodi ugualmente specifici con la stessa precedenza.


0

Il compilatore Java fornisce il tipo di classe più derivato per assegnare null.

Ecco l'esempio per capirlo:

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

uscita: Classe B.

d'altro canto:

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

Risultato: il metodo fun (C) è ambiguo per il tipo MyTest

Spero che possa aiutare a capire meglio questo caso.


0

Fonte: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Concetto: metodo più specifico
Spiegazione: se più di un metodo membro è accessibile e applicabile a una chiamata di metodo, è necessario sceglierne uno per fornire il descrittore per l'invio del metodo in fase di esecuzione. Il linguaggio di programmazione Java utilizza la regola che viene scelto il metodo più specifico. Prova a trasmettere il valore nullo a un tipo specifico e il metodo che desideri verrà chiamato automaticamente.


Nel primo esempio, String estende Object, quindi "più specifico" è il metodo che prende String, ma nel secondo esempio String e StringBuffer estendono entrambi Object, quindi sono ugualmente specifici e quindi il compilatore non può fare una scelta
David Kerr

-1

Direi nessuno dei due. NULL è uno stato, non un valore. Dai un'occhiata a questo link per maggiori informazioni su questo (l'articolo si applica a SQL, ma penso che aiuti anche con la tua domanda).


nullè decisamente un valore, come definito nel JLS.
Sotirios Delimanolis
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.