Come incapsulare i parametri del metodo in Java?

0

Una query HTTP può essere parametrizzata in un elenco di coppie come name=value che può essere incapsulato in generale come Map<String, String> poiché una query HTTP GET è una coppia di stringhe. Potrei aver bisogno di qualcosa per qualsiasi combinazione di oggetti e primitive. Un metodo varargs ad esempio update(String s1...) potrebbe essere scritto in generale come update(Map<String, String>) dove la chiave è il nome e il valore è i dati.

Quindi mi chiedo se potrebbe essere utile usare una definizione come Map<K, V> invece di scrivere un oggetto personalizzato in questo caso avvolgendo gli specifici 3 parametri in una classe nei loro tipi specifici che non possono essere riutilizzati mentre un Map<K, V> può essere usato per incapsulare qualsiasi 2-tuple quando dobbiamo scrivere una coppia come Integer i o int i o int i=0 .

Ma penso che funzionerà solo per le classi e non per i tipi primitivi, quindi i tipi primitivi devono essere racchiusi nei loro oggetti quindi devo anche inscatolare un int in un Integer affinché questo funzioni anche con i tipi primitivi .

Esiste un modo migliore di Map<K, V> per incapsulare i parametri del metodo quando dobbiamo selezionare un elenco di tipi sconosciuti di lunghezza e sconosciuto in cui i tipi potrebbero essere primitivi o oggetti?

Quindi, invece di essere specifico sui tipi e sui loro valori nel metodo, posso usare un Map<K, V> invece di un elenco di coppie nominate come String s, int i, Object o...

    
posta Niklas Rosencrantz 22.02.2014 - 04:55
fonte

1 risposta

3

I tipi di dati astratti non sono buone cose in Java per passare oggetti o parametri complessi.

Digita sicurezza

Consente di confrontare due bit di codice

String foo(Integer a, Integer b, String s) {
  if(a.compareTo(b) == 0) {
    return s;
  } else {
    return "" + (a - b); // just as an example
  }
}

vs

String foo(Map<String, Object> m) {
  if(m.get("a").compareTo(m.get("b")) == 0) {
    return (String)m.get("s");
  } else {
    return "" + (((Integer)m.get("a") - (Integer)m.get("b"));
  }
}

Nel secondo esempio, dobbiamo usare Object come valore perché è l'unico oggetto che String e Integer condividono in comune. Ciò significa che tutti gli oggetti più in basso devono essere lanciati. Questo ha la netta possibilità di lanciare un ClassCastException ( javadoc ) ovunque o in qualsiasi punto del codice ... a meno che non aggiunga una tonnellata di codice della piastra della caldaia per impedirlo (condizioni di protezione per instanceof dappertutto).

Quel che è peggio è che quelle eccezioni (se non controlli tutto - e i percorsi che segui se lo fai) sono errori di runtime. Gli errori dei tuoi tipi non verranno trovati finché non eseguirai il programma anziché quando lo compili. Gli errori rilevati durante la compilazione sono più facili da correggere rispetto a quelli rilevati in fase di esecuzione. Del resto, gli strumenti di analisi statica e le ispezioni che la maggior parte degli IDE ti forniranno li prenderanno per te mentre li digiti (e lanciano orrendi avvertimenti sul tentativo di farlo con il tipo di dati astratti).

sovraccarichi

Passare tutti gli argomenti come un singolo ADT significa che hai un unico metodo. Nell'esempio sopra, cosa succede se hai:

String foo(Integer a, Integer b) { ... }
String foo(Integer a, Integer b, String s) { ... }
String foo(Integer a, Integer b, String s, String t) { ... }

come modi diversi per chiamare la funzione. Con il passaggio di un ADT, hai solo

String foo(Map<String, Object> m) { ... }

Non c'è modo di differenziare i sovraccarichi. Questo porta al codice che assomiglierà a:

String foo(Map<String, Object> m) {
  if(m.containsKey("t")) { ... }
  else if(m.containsKey("s")) { ... }
  else { ... }
}

La complessità della funzione passerà attraverso il tetto (a meno che non inizi ad estrarre quelli ad altri metodi che in qualche modo indicano il tipo con cui hanno a che fare e la mia testa stia cominciando a ferire pensando alla notazione ungherese che stai per attenersi ai nomi dei metodi).

Questo ulteriore diventa problematico quando hai tipi diversi che sono argomenti validi.

String foo(Integer a, Integer b, String s) { ... }
String foo(BigInteger a, BigInteger b, String s) { ... }

Sicurezza di digitazione

Molto tempo fa, ero un programmatore di Perl - nei giorni precedenti alla scoperta di OOP in perl ( bless come riferimento). Il modo solo di passare attorno a oggetti complessi era con %hashes e @arrays . E tu dovevi tirare fuori le chiavi dell'hash. C'erano numerosi bug che potevano essere rilevati solo in fase di esecuzione (e le gioie di autovivication hanno fatto in modo che a volte difficile).

Un semplice errore di battitura in una chiave significherebbe che alcuni parametri non sono stati trovati (quando erano lì) o sono stati appena creati nel punto sbagliato e passati a un altro metodo.

String plusOne(Map<String, Integer> m) {
  if(m.containsKey("type") && m.containsKey("1") {
    return m.get("typo") + m.get("I");
  }
  return 0;
}

Questi non sono bug divertenti da trovare. Nessun bug è davvero divertente, ma questi bug ti schizzano in faccia perché sono completamente prevenibili se tu avessi appena usato un elenco di parametri appropriato e lavorato con la lingua e il compilatore.

Chiamare il packaging

Finora, ho parlato dei pericoli nel metodo stesso. Che dire del lavoro che il callee deve fare.

System.out.println(foo(1,2,"bar"));

E abbiamo finito con la semplice lista dei parametri.

Map<String, Object> m = new HashMap<String, Object>();
m.put("a", 1);
m.put("b", 2);
m.put("s", "bar");
System.out.println(foo(m));

Ora, vuoi eseguire il debug di questo? Cosa stai passando in foo? Devi guardare indietro nel codice e seguirlo. Oltre ad essere un blocco di codice significativamente più grande per eseguire qualsiasi chiamata di metodo di questo tipo, è anche ovvio che cosa sta entrando in questo.

refactors

foo ora prende Double s anziché Integer s. Si modifica la firma del metodo e si correggono tutti gli errori di compilazione. Li hai trovati tutti e compila correttamente (vedi tipo sicurezza sopra).

Tuttavia, la versione della mappa funziona altrettanto bene con Integer come con Double . non puoi scoprire se hai corretto il refactoring o meno.

Perché sono insieme in Map ?

Questa è più una domanda filosofica. Perché sono questi oggetti in una mappa insieme? Quale attributo comune condividono? Se realmente fanno parte di un'altra struttura di dati

class Cell {
  int x, y;
  String value;
}

trasformali in una struttura dati adeguata che si incastri. Se non sono cose che sono onestamente legate l'una all'altra, beh ... no.

Mettere le cose insieme in una struttura le rende legate l'una all'altra nella nostra mente, anche se non lo sono. Se non lo sono, aggiunge una significativa ginnastica mentale per mantenere le cose sono correlate tra loro e quelle che non sono a parte.

    
risposta data 22.02.2014 - 17:59
fonte

Leggi altre domande sui tag