2012-05-16 9 views
6

Dato questo codice Java, questo uscite 0 e 4:Il costruttore Java non è così intuitivo. O forse non è Java, è C# che non è intuitivo

class A{ 
    A() { print(); } 
    void print() { System.out.println("A"); } 
} 

class B extends A{ 
    int i = Math.round(3.5f); 

    public static void main(String[] args){ 
     A a = new B(); 
     a.print(); 
    } 
    void print() { System.out.println(i); } 
} 

E con questo codice identico C#, questo emette 4 e 4

using System;

class A{ 
    internal A() { print(); } 
    virtual internal void print() { Console.WriteLine("A"); } 
} 

class B : A{ 
    int i = (int) Math.Round(3.5f); 

    public static void Main(string[] args){ 
     A a = new B(); 
     a.print(); 
    } 
    override internal void print() { Console.WriteLine(i); } 
} 

Anche se calcoliamo che l'uscita dovrebbe essere 4 e 4 su Java, ma la risposta è in realtà 0 e 4 su Java. Poi ho provato in C#, la risposta è 4 e 4

Che cosa dà? Java razionale è, durante la costruzione di B, A è ancora in fase di inizializzazione (di conseguenza, suppongo che B stia ancora inizializzando se Java ha detto che A è ancora in fase di inizializzazione), quindi il valore predefinito dovrebbe essere 0. Quindi l'output è 0 e 4 in Java.

Perché il comportamento del costruttore C# differisce da Java o viceversa?

+4

Farò in modo che altre persone rispondano al "perché", ma questo tipo di problemi è il motivo per cui in genere è considerata una cattiva pratica chiamare i metodi virtuali dai costruttori. –

+1

+1 per considerare di chiamare un metodo virtuale da costruttore una pratica scadente. L'output non è prevedibile dato che diverse lingue hanno una diversa filosofia di progettazione – Hao

+0

@HovercraftFullOfEels Lo sento anch'io. Volevo trovare una soluzione però. Come rendere più chiaro l'intento del codice o il comportamento del codice, date le sfumature delle lingue. Potrebbe non esserci una soluzione chiara a questo problema, ma forse ci sono alcune buone pratiche in giro, ad es. considerando che il metodo virtuale non dovrebbe essere chiamato nel costruttore – Hao

risposta

8

Sta succedendo a causa delle differenze nell'ordinamento dell'inizializzazione dell'oggetto nei costruttori.

Quello che sta accadendo in Java:

  • (vuoto, implicito) Costruttore di B si chiama
  • superclasse construtor di A è chiamato (stampe 0 dal momento che i è Non inizializzato)
  • i è inizializzata dopo il costruttore della superclasse
  • print() viene chiamato (stampe 4)

Quello che sta accadendo in C#:

  • (vuoto, implicito) Costruttore di B si chiama
  • i viene inizializzato prima chiamare il costruttore della superclasse
  • superclasse construtor di A è chiamato (stampe 4 da quando già inizializzata)
  • print() viene chiamato (stampe 4)

Né è giusto o sbagliato - è solo una differenza nel modo in cui il compilatore o esegue le operazioni di costruzione. Personalmente ritengo che l'ordinamento Java sia marginalmente più logico, perché per me è logico che la superclasse sia completamente costruita prima che avvenga l'inizializzazione della sottoclasse.

In entrambi i casi, poiché la logica può diventare piuttosto complicata, suggerisco di evitare di chiamare i metodi virtuali durante la costruzione di oggetti in generale.

+0

provo a eseguire il debug per Java e conosco solo passo per passo come è stato eseguito il programma, ma sfortunatamente non ho compilato il compilatore C#. +1 per lo pseudo-codice. – Crazenezz

+0

@Crazenezz http://ideone.com – Hao

+0

@Hao: Bello, ma nessuna funzione di debug :-) – Crazenezz

1

L'ordine di inizializzazione in Java:

1. stoccaggio dell'istanza viene cancellato a zero, automaticamente impostare tutte le primitive nell'oggetto ai valori predefiniti (zero per i numeri e l'equivalente per boolean e char) e i riferimenti a null.

2. Viene chiamato il costruttore della base class A. Chiamerà il metodo print in class B, poiché è un metodo sottoposto a override. i è 0 in questo momento.

3. L'inizializzazione del membro della classe B viene eseguita. Quindi i è 4 ora.

Per non generare questo tipo di sorpresa, non chiamare metodi non statici o non privati ​​in un costruttore, poiché potrebbero essere sovrascritti nelle classi derivate.

Problemi correlati