Nozioni di base sulla dichiarazione degli oggetti in Java

1

C'è un caso in cui un oggetto viene dichiarato senza una chiamata al costruttore? come in, ad esempio:

ArrayList<Integer> grades;

O è sempre il caso che ArrayList<Integer> grades (come nel nostro esempio qui) sia sempre seguito da una chiamata a un costruttore come in,

ArrayList<Integer> grades = new ArrayList<Integer>();

Se quest'ultimo è il caso, cioè una dichiarazione di un oggetto è sempre seguita dalla sua inizializzazione con un costruttore, quindi perché non è

ArrayList<Integer> grades;

implica già

ArrayList<Integer> grades = new ArrayList<Integer>();

In caso contrario, quando viene dichiarato un oggetto senza una chiamata immediata al costruttore che lo segue?

È il caso che

ArrayList<Integer> grades;

può essere inizializzato con qualcosa di diverso da

= new ArrayList<Integer>();
    
posta Zvi 13.03.2015 - 01:26
fonte

5 risposte

6

Considera due concetti distinti: oggetti e riferimenti.

I riferimenti "puntano" agli oggetti in memoria, ma non sono gli oggetti stessi. In Java, ci riferiamo sempre e controlliamo gli oggetti attraverso riferimenti a loro. In Java, non si memorizza mai l'oggetto stesso nella variabile, ma solo l'indirizzo in memoria (non esattamente - dipende dall'implementazione - ma si ottiene l'idea).

Quindi dichiarando ArrayList<Integer> grades; , ciò che fai è creare un riferimento , che è in grado di puntare a oggetti di tipo ArrayList<Integer> (e qualsiasi sottotipo).

E dichiarando new ArrayList<Integer>() , ciò che fai è creare un nuovo oggetto in memoria, e avere un "puntatore" a esso restituito (non l'effettivo dati oggetto).

Quindi, dichiarando ArrayList<Integer> grades = new ArrayList<Integer>(); , crei un ArrayList<Integer> da qualche parte in memoria, e assegni il suo indirizzo di memoria (di nuovo, una specie di) alla variabile grades .

Dichiarando solo ArrayList<Integer> grades; crea un riferimento vuoto che non punta da nessuna parte.

    
risposta data 13.03.2015 - 02:18
fonte
1

Sì, i riferimenti agli oggetti possono essere inizializzati senza costruttori o assegnazioni visibili. Questo effetto viene spesso ottenuto utilizzando contenitori Iniezione di dipendenza come Guice o Spring.

class Foo {
  @Inject
  Integer bar;
};

Foo foo = container.create(Foo.class);
assert foo.bar != null;

Se questa classe viene istanziata nel contesto del contenitore di iniezione, la sua barra di campo otterrà un valore senza mai essere assegnato direttamente. Questi di solito usano la riflessione per impostare i valori, quindi mentre l'assegnazione non è esplicita, accade ancora. Non penso che possiamo fare lo stesso trucco per le variabili locali.

    
risposta data 13.03.2015 - 03:47
fonte
1

Per semplicità, i designer Java hanno deciso che le stringhe, nonostante non fossero primitive, possono essere dichiarate e inizializzate come tali.

String s = "test";

che è uguale a

String s = String("test");

Qualcosa di simile hanno fatto con gli array:

int[] numbers = {1,2,3};

Questi sono esempi di oggetti inizializzati senza chiamare esplicitamente i loro costruttori con new() . ma puoi essere certo che il costruttore venga chiamato comunque.

    
risposta data 13.03.2015 - 13:19
fonte
1

Is there a case when an object is declared without a call to the constructor?

Certamente c'è.
Tutti di loro, infatti.

Dichiarando una variabile oggetto crea una posizione ("indirizzo") in cui il programma può scrivere in seguito un riferimento ("puntatore") a un'istanza di un oggetto [del tipo specificato].

Istanziare un oggetto è l'azione che chiama il costruttore pertinente.

Il ...

Type name = expression ; 
La sintassi

... è solo un modo compatto di fare la dichiarazione e l'istanza come un "unico elemento", ma non è funzionalmente diverso da ...

Type name [ = null ] ; 
name = new Type( argument, ...); 

In secondo luogo:

Is it the case that "ArrayList<Integer> grades" can be initialized by anything other than "= new ArrayList<Integer>()".

Ancora, Sì.
Puoi inizializzare la variabile chiamata voti al valore di qualsiasi espressione del tipo di dati "ArrayList", ad esempio.

ArrayList<Integer> grades = class.getStudentGrades(); 
    
risposta data 13.03.2015 - 13:08
fonte
0

then why isn't

ArrayList<Integer> grades;

already implies

ArrayList<Integer> grades = new ArrayList<Integer>();

Per prima cosa, leggi la risposta di @ AvivCohn sui riferimenti.

In secondo luogo, oltre alla risposta di @ Basilev sull'iniezione di dipendenza, ci sono anche due ragioni per cui un implicito la chiamata a new potrebbe non essere desiderata (quanto segue sono concetti specifici di Java e non di programmazione in generale):

  1. La costruzione di un nuovo oggetto è "costoso"

    • modifica: come @MikePartridge ha menzionato utile nei commenti, non intendo "costoso" nel senso della creazione di un oggetto per sé , ma se un oggetto può essere creato con solo un new MyObject(...) one-liner o no. Vedi questo link che dissipa con il mito che la creazione dell'oggetto è costoso .
    • Il tuo esempio di ArrayList sarà un cattivo esempio di mostrare perché è così, ma considera un oggetto che richiede l'impostazione di molti campi, ognuno dei quali può richiedere una propria costosa costruzione. È possibile che i valori dei campi debbano essere recuperati da un database o da chiamate di procedure remote. Ecco perché abbiamo builder o factory modelli che costruiscono l'oggetto per noi, prima di assegnarlo al riferimento.
    • Questo è correlato all'iniezione di dipendenza.
    • Naturalmente, implicitamente, ci sarà un new MyObject(...) chiamato nel builder / factory, ma l'oggetto verrà inizializzato correttamente prima dell'assegnazione.
    • In una nota correlata, a volte il costruttore diventa quindi un costruttore di private che segue il modello di builder, poiché i chiamanti all'istanza di detta classe non hanno bisogno di sapere come l'oggetto viene inizializzato.
  2. Ci possono essere diversi modi per assegnare un oggetto a un riferimento

    • Qui ci sono in realtà due esempi per dimostrarlo, ma ho intenzione di abbatterlo con lo stesso motivo. In entrambi gli esempi, test può essere impostato solo una volta a causa del suo modificatore final , che impedisce errori di programmazione semplici come la sovrascrittura del riferimento (e quindi eventualmente il confronto errato). In ogni caso, essi (si spera) mostrano perché non può esserci un incarico implicito in new String() , e perché non è desiderato. Ancora una volta, la chiamata a new (appena dichiarando "..." lo fa in Java) viene eseguita esplicitamente durante l'assegnazione, invece che implicita come hai chiesto nella tua domanda.
    • switch / if logic per determinare il valore da impostare

      final String test;
      switch (value) {
      case A:
          test = "some-text";
          break;
      case B:
          test = "more-text-please";
          break;
      ...
      }
      
    • try-catch per eseguire assegnazioni alternative in caso di eccezioni

      final String test;
      try {
          test = someMethodThatReturnsAStringOrMayThrowAnException();
      } catch (SomeException e) {
          logThisException(e);
          test = "alternate-value";
      }
      
risposta data 13.03.2015 - 07:29
fonte

Leggi altre domande sui tag