Domanda

Dai un'occhiata a questo codice.

// Print object and recurse if iterable
private static void deep_print(Object o) {
  System.out.println(o.getClass().toString() + ", " + o.toString());

  boolean iter = false;
  Iterable<?> i1 = null;
  Object[] i2 = null;

  if (o instanceof Iterable<?>) {
    iter = true;
    i1 = (Iterable<?>) o;
  } else if (o instanceof Object[]) {
    iter = true;
    i2 = (Object[]) o;
  }

  if (iter) {
    for (Object o_ : i2 == null ? i1 : i2) deep_print(o_); // ERROR: Can only iterate over an array or an instance of java.lang.Iterable
  }

Io so come risolverlo. Voglio solo sapere perché succede. Non dovrebbe il compilatore semplicemente visualizzate tutte le possibili uscite?

È stato utile?

Soluzione

Il tipo di risultato statico del (i2 == null) ? i1 : i2 espressione è l'antenato comune i1 e i2 che è oggetto. Una dichiarazione for richiede l' tipo statico dell'espressione essere o un Iterable o un tipo di matrice. Non è questo il caso, in modo da ottenere un errore di compilazione.

Ora, se ti stai chiedendo perché non il compilatore dedurre che (i2 == null) ? i1 : i2 sarà sempre un array o un Iterable:

  1. La specifica del linguaggio Java (JLS) non consente questo.
  2. Se il JLS lo permetteva (ma non lo richiedono), quindi diversi compilatori si comporterebbe in modo diverso, a seconda di quanto fossero in teoremi. BAD.
  3. Se la JLS richiesto, allora il compilatore deve incorporare un dimostratore sofisticato teorema `1 (BAD SLOW), e si esegue in il "piccolo inconveniente" di risolvere l'arresto Problema 2 (male male male).
  4. In realtà il compilatore deve conoscere quale dei due tipi di tipo dell'espressione è perché deve generare codice diverso in ciascun caso.

Ipoteticamente, se il sistema di tipo Java è stato un po 'diverso, questo caso particolare potrebbero essere gestito meglio. Nello specifico, se Java supportato allora sarebbe possibile dichiarare o come "sia una matrice o un oggetto iterabile" ... e il ciclo for sarebbero tipo controllabile essere.


1 - Supponiamo che o era stato inizializzato come o = (x * x < 0) ? new Object() : new Object[0]. Determinare che ciò comporta sempre un'istanza Object[] comporta una piccola prova coinvolge il fatto che il quadrato di un numero (reale) non è negativo. Questo è un semplice esempio, è possibile costruire esempi arbitrariamente complicati da prove difficili arbitrari.

2 - l'arresto problema è matematicamente dimostrato di essere una funzione incomputable. In altre parole, esistono funzioni in cui è matematicamente impossibile dimostrare se essi terminano.

Altri suggerimenti

Per illustrare la risposta di Stephen C, si consideri il seguente codice:

void test() {
      Iterable<Integer> i1 = new ArrayList<Integer>();
      Object[] i2 = { 1, 2, 3 };      
      method1(false ? i1 : i2);
      method1(true ? i1 : i2);  
}

void method1(Object o) {
    System.out.println("method1(Object) called");
}

void method1(Object[] o) {
    System.out.println("method1(Object[]) called");
}

void method1(Iterable<?> o) {
    System.out.println("method1(Iterable<?>) called");
}

Questa è l'uscita di test ():

method1(Object) called
method1(Object) called

Poiché sovraccarico metodo viene eseguito al momento della compilazione, si può vedere che il tipo statico dell'espressione operatore ternario è oggetto, poiché i tipi di operandi differiscono. Pertanto, quando si fa:

for (Object o_ : i2 == null ? i1 : i2)

si sta veramente chiedendo al compilatore di generare un ciclo foreach su un oggetto, che è illegale.

in questo caso, l'espressione condizionale ha il tipo di estremo superiore dei due tipi, che è Object, e il ciclo foreach non funziona sul tipo Object

Si dovrebbe aggiungere due metodi di overload deepPrint (Object []) e deepPrint (Iterator i) e fare l'invio nel vostro deepPrint (oggetto Object). Sì a causa della natura di come il per / ogni ciclo funziona avrete bisogno di copiare e incollare lo stesso codice con lievi modifiche.

Cercando di mettere tutto ciò in un unico grande metodo è un odore.

È possibile evitare la duplicazione del codice avvolgendo l'oggetto [] in un Arrays.asList (Object []) e poi avere un Iterable in tutti i casi. Sì, è leggermente più lento di lavorare su un array, ma ha il vantaggio di essere DRY, che deve sempre essere la prima approssimazione, IMHO.

Quindi, si finirebbe con qualcosa di simile:

Iterable<?> it = null;
if (o instanceof Iterable<?>) {
    it = (Iterable<?>) o;
} else if (o instanceof Object[]) {
    it = Arrays.asList((Object[]) o);
}

if (it != null) {
   for (Object o_ : it) deep_print(o_);
}

Per semplicità (si veda la risposta di Stephen C per come è più semplice per gli scrittori del compilatore, ma è senza dubbio un disegno linguaggio più semplice come pure) l'operatore ternario assume il tipo di essere il minimo comune denominatore tra i due tipi di ritorno, piuttosto che accomodante uno dei due tipi restituiti. Consideriamo il caso di un metodo di sovraccarico. Il metodo giusto è determinato e tempo di compilazione. Ciò significa che il compilatore deve essere in grado di prendere una decisione sul tipo di ritorno dichiarato in fase di compilazione, e non può ritardare la decisione di correre il tempo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top