Perché la creazione dell'istanza è così com'è?

17

Ho imparato C # nel corso degli ultimi sei mesi circa e ora sto scavando in Java. La mia domanda riguarda la creazione di istanze (in entrambi i linguaggi, in realtà) ed è più di: Mi chiedo perché l'abbiano fatto in quel modo. Prendi questo esempio

Person Bob = new Person();

C'è una ragione per cui l'oggetto viene specificato due volte? Ci sarebbe mai un something_else Bob = new Person() ?

Sembrerebbe che se stavo seguendo da una convenzione sarebbe più simile a:

int XIsAnInt;
Person BobIsAPerson;

O forse uno di questi:

Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();

Suppongo di essere curioso di sapere se c'è una risposta migliore di "è proprio così che è fatta".

    
posta Jason Wohlgemuth 07.02.2015 - 20:29
fonte

7 risposte

52

Would there ever be a something_else Bob = new Person()?

Sì, a causa dell'ereditarietà. Se:

public class StackExchangeMember : Person {}

Quindi:

Person bob = new StackExchangeMember();
Person sam = new Person();

Anche Bob è una persona e, perbacco, non vuole essere trattato diversamente da chiunque altro.

Inoltre, potremmo dotare Bob di superpoteri:

public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}

IModerator bob = new StackOverFlowModerator();

E così, perbacco, non starà per essere trattato in modo diverso rispetto a qualsiasi altro moderatore. E a lui piace sgattaiolare attorno al forum per tenere tutti in fila in incognito:

StackExchangeMember bob = new StackOverFlowModerator();

Poi, quando trova un povero 1 ° poster, getta via il suo mantello dell'invisibilità e si lancia.

((StackOverFlowModerator) bob).Smite(sam);

E poi può recitare tutti gli innocenti e cose del genere in seguito:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();
    
risposta data 08.02.2015 - 00:54
fonte
34

Prendiamo la prima riga di codice ed esaminiamola.

Person Bob = new Person();

Il primo Person è una specifica del tipo . In C #, possiamo fare a meno di ciò dicendo semplicemente

var Bob = new Person();

e il compilatore deduce il tipo della variabile Bob dalla chiamata del costruttore Person() .

Ma potresti voler scrivere qualcosa del genere:

IPerson Bob = new Person();

Dove non rispetti l'intero contratto API di Person, ma solo il contratto specificato dall'interfaccia IPerson .

    
risposta data 07.02.2015 - 20:56
fonte
21
  1. Questa sintassi è praticamente un retaggio del C ++, che, tra l'altro, ha entrambi:

    Person Bob;
    

    e

    Person *bob = new Bob();
    

    Il primo a creare un oggetto nell'ambito corrente, il secondo a creare un puntatore a un oggetto dinamico.

  2. Sicuramente puoi avere something_else Bob = new Person()

    IEnumerable<int> nums = new List<int>(){1,2,3,4}
    

    Qui stai facendo due cose diverse, affermando il tipo della variabile locale nums e dici di voler creare un nuovo oggetto del tipo 'Elenco' e metterlo lì.

  3. C # tipo di accordo con te, perché il più delle volte il tipo di variabile è identico a cosa ci metti dentro quindi:

    var nums = new List<int>();
    
  4. In alcune lingue fai del tuo meglio per evitare di indicare i tipi di variabili come in F # :

    let list123 = [ 1; 2; 3 ]
    
risposta data 07.02.2015 - 20:57
fonte
3

C'è un'enorme differenza tra int x e Person bob . Un int è un int è un int e deve sempre essere un int e non può mai essere qualsiasi cosa diversa da int . Anche se non si inizializza int quando lo si dichiara ( int x; ), è ancora un int impostato sul valore predefinito.

Quando dichiari Person bob , tuttavia, c'è molta flessibilità su cosa il nome bob potrebbe effettivamente fare riferimento in un dato momento. Potrebbe riferirsi a un Person o potrebbe riferirsi ad un'altra classe, ad es. %codice%, derivato da Programmer ; potrebbe anche essere Person , riferendosi a nessun oggetto.

Ad esempio:

  Person bob   = null;
  Person carol = new Person();
  Person ted   = new Programmer();
  Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();

I progettisti di linguaggi avrebbero sicuramente potuto creare una sintassi alternativa che lo sarebbe stata hanno realizzato la stessa cosa di null in meno simboli, ma avrebbero comunque dovuto consentire Person carol = new Person() (o fare qualche regola strana rendendo quel particolare uno dei quattro esempi sopra illegale). Erano più preoccupati di mantenere la lingua "semplice" rispetto alla scrittura codice estremamente conciso. Questo potrebbe aver influenzato la loro decisione di non fornire la sintassi alternativa più breve, ma in ogni caso, non era necessario e non lo hanno fornito.

    
risposta data 08.02.2015 - 22:22
fonte
1

Le due dichiarazioni possono essere diverse ma spesso sono le stesse. Un modello consigliato comune in Java è simile a:

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

Queste variabili list e map vengono dichiarate utilizzando le interfacce List e Map mentre il codice crea un'istanza di implementazioni specifiche. In questo modo, il resto del codice dipende solo dalle interfacce ed è facile scegliere istanze di implementazione differenti, come TreeMap , poiché il resto del codice non può dipendere da alcuna parte dell'API HashMap che è al di fuori dell'interfaccia Map .

Un altro esempio in cui i due tipi differiscono è in un metodo factory che seleziona una sottoclasse specifica da istanziare, quindi lo restituisce come tipo base in modo che il chiamante non debba essere a conoscenza dei dettagli dell'implementazione, ad esempio una scelta "politica".

L'inferenza del tipo può correggere la ridondanza del codice sorgente. Ad esempio in Java

List<String> listOne = Collections.emptyList();

costruirà il giusto tipo di lista grazie all'inferenza del tipo e alla dichiarazione

static <T> List<T> emptyList(); 

In alcune lingue, l'inferenza di tipo va oltre, ad esempio in C ++

auto p = new Person();
    
risposta data 07.02.2015 - 20:58
fonte
1

In parole semplici:

  • Separare la dichiarazione dalla creazione di istanze aiuta a disaccoppiare chi utilizza gli oggetti da chi li crea
  • Quando lo fai, il poliporfismo è abilitato poiché, finché il tipo istanziato è un sottotipo del tipo di dichiarazione, tutto il codice che utilizza la variabile funzionerà
  • Nelle lingue strongmente tipizzate devi dichiarare una variabile dichiarando il suo tipo, semplicemente var = new Process() non stai dichiarando prima la variabile.
risposta data 08.02.2015 - 01:26
fonte
0

Riguarda anche il livello di controllo su ciò che sta accadendo. Se la dichiarazione di un oggetto / variabile chiama automaticamente un costruttore, ad esempio, se

Person somePerson;

era automaticamente uguale a

Person somePerson = new Person(blah, blah..);

quindi non saresti mai in grado di utilizzare (ad esempio) metodi statici di fabbrica per istanziare oggetti anziché costruttori predefiniti, cioè, ci sono volte in cui non vuoi chiamare un costruttore per una nuova istanza di oggetto.

Questo esempio è spiegato in Joshua Bloch 's Efficace Java (elemento 1 abbastanza ironico!)

    
risposta data 07.02.2015 - 21:21
fonte

Leggi altre domande sui tag