Fatta eccezione per i già citati problemi relativi allo stile di programmazione e all'abuso di ereditarietà, esiste un altro problema sottile: le classi interne e le istanze di classi anonime (non statiche) fungono da chiusure. Ciò significa che sono che mantengono un riferimento implicito all'istanza della classe che lo contiene. Ciò può impedire la raccolta dei dati inutili e, infine, una perdita di memoria.
Dato un pezzo esempio di codice sorgente:
public interface Inner {
void innerAction();
}
public class Outer {
public void methodInOuter() {}
private Inner inner = new Inner() {
public void innerAction() {
// calling a method outside of scope of this anonymous class
methodInOuter();
}
}
}
Cosa succede al momento della compilazione, è che il compilatore crea un file di classe per la nuova sottoclasse anonima di Inner
che ottiene un campo cosiddetto sintetico con la riferimento all'istanza della classe Outer
. Il bytecode generato sarà meno equivalente a qualcosa di simile:
public class Outer$1 implements Inner {
private final Outer outer; // synthetic reference to enclosing instance
public Outer$1(Outer outer) {
this.outer = outer;
}
public void innerAction() {
// the method outside of scope is called through the reference to Outer
outer.methodInOuter();
}
}
Tale acquisizione del riferimento all'istanza allegando accade anche per classi anonime che mai effettivamente accedono qualsiasi dei metodi o campi della classe contenitrice, come l'elenco DBI (double-blind initialized) nella tua domanda.
Ciò comporta il fatto che l'elenco DBI mantiene un riferimento all'istanza che lo contiene fintanto che esiste, impedendo che l'istanza che la racchiude venga raccolta. Supponiamo che l'elenco DBI si trovi a vivere a lungo nell'applicazione, ad esempio come parte del modello nel pattern MVC, e la classe racchiusa catturata è ad esempio un JFrame
, che è una classe piuttosto ampia con molti campi. Se hai creato un paio di elenchi DBI, si otterrebbe una perdita di memoria molto rapidamente.
Una possibile soluzione sarebbe utilizzare DBI solo nei metodi statici, perché non è disponibile tale istanza di inclusione nel relativo ambito.
D'altra parte, vorrei ancora sostenere che l'utilizzo di DBI non è ancora necessario nella maggior parte dei casi. Per quanto riguarda la lista che unisce, creerei un semplice metodo riutilizzabile, che non è solo più sicuro, ma anche più conciso e chiaro.
public static <T> List<T> join(List<? extends T> first, List<? extends T> second) {
List<T> joined = new ArrayList<>();
joined.addAll(first);
joined.addAll(second);
return joined;
}
E poi il codice del client diventa semplicemente:
List<String> newList = join(listOne, listTwo);
Ulteriori approfondimenti: https://stackoverflow.com/a/924536/1064809
può essere che confonderà altri sviluppatori :-), dopotutto non è una linea codice. – Subin
possibile duplicato di Initialization Double Brace [Efficiency of Java "?] (Http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) – assylias
@assylias: la domanda collegata a domanda principalmente sulle prestazioni. Non penso sia un duplicato –