instancier paresseusement un champ final
Question
Est-il possible d'instancier paresseusement un champ final?
Le code suivant ne compile pas:
public class Test{
private final Connection conn;
public Connection getConnection(){
if(conn==null){
conn = new Connection();
}
return conn;
}
}
Y at-il une alternative?
La solution 2
Voici une façon que vous pouvez le faire en utilisant mémoïsation (avec appelables):
Classe Mémo:
public class Memo<T> {
private T result;
private final Callable<T> callable;
private boolean established;
public Memo(final Callable<T> callable) {
this.callable = callable;
}
public T get() {
if (!established) {
try {
result = callable.call();
established = true;
}
catch (Exception e) {
throw new RuntimeException("Failed to get value of memo", e);
}
}
return result;
}
}
Maintenant, nous pouvons créer une conn finale!
private final Memo<Connection> conn = new Memo<Connection>(
new Callable<Connection>() {
public Connection call() throws Exception {
return new Connection();
}
});
public Connection getConnection() {
return conn.get();
}
Autres conseils
Non. Le point d'un champ final est qu'il est fixé une fois, lors de la construction, et ne changera jamais par la suite. Comment pourrait le compilateur ou la machine virtuelle rien savoir utile sur votre cas dans conn
? Comment serait-il savoir que cette propriété ne devrait être en mesure de le mettre, et non une autre méthode?
Peut-être si vous voulez ce que vous expliquez la sémantique d'être, nous pourrions arriver à un altérant. Vous pourriez éventuellement avoir une interface « fournisseur » représentant un moyen d'aller chercher une valeur, puis un des procurations à MemoizingProvider
un autre fournisseur, mais une seule fois, la mise en cache de la valeur autrement. Ce ne serait pas en mesure d'avoir un champ final pour la valeur mise en cache non plus, mais au moins il ne serait en un seul endroit.
La réponse de dhiller est le bug de verrouillage revérifié classique, ne pas utiliser.
Comme dit Jon Skeet, non, il n'y a pas.
Interprétation de votre exemple de code que vous pouvez faire quelque chose comme ceci:
public class Test{
private final Object mutex = new Object(); // No public locking
private Connection conn;
public Connection getConnection(){
if(conn==null){
synchronized (mutex) {
if(conn==null){
conn = new Connection();
}
}
}
return conn;
}
}
Comme une note de côté, il est possible de changer un champ final. Au moins champs d'instance. Vous avez juste besoin de réflexion:
import java.lang.reflect.Field;
public class LazyFinalField {
private final String finalField = null;
public static void main(String[] args) throws Exception {
LazyFinalField o = new LazyFinalField();
System.out.println("Original Value = " + o.finalField);
Field finalField = LazyFinalField.class.getDeclaredField("finalField");
finalField.setAccessible(true);
finalField.set(o, "Hello World");
System.out.println("New Value = " + o.finalField);
}
}
Original Value = null
New Value = Hello World