metodi virtuali Java: funzione o bug?
-
26-10-2019 - |
Domanda
Prendete questa classe di base:
public abstract class XMPPSubservice
{
protected XMPPService mTheService;
protected XMPPSubservice(Context context)
{
Intent intent = new Intent(context, XMPPService.class);
context.startService(intent);
}
public void onServiceInstance(XMPPService service) {
// TODO Auto-generated method stub
mTheService = service;
}
}
E questa classe derivata:
public class PublicDataSubservice extends XMPPSubservice
{
private final SomeObject mObj = new SomeObject();
public PublicDataSubservice(Context context) {
super(context);
}
@Override
public void onServiceInstance(XMPPService service)
{
super.onServiceInstance(service);
mObj.doSomethingWith(mTheService);
}
}
L'obiettivo è stato quello di chiamare solo mObj.doSomethingWith (mTheService); dopo la mTheService diventato valida (che è accaduto nella classe base). Cosa era sempre sputato NPE alla linea mObj. Posso capire perché quello che è successo, ma sembra wonky a me. Quindi questo è un bug o una caratteristica di DVM? Che ne dite di JVM?
Soluzione
Questo è del tutto corretto, e si sarebbe verificato in "vanilla" Java troppo.
initializers variabile di istanza vengono eseguiti solo all'inizio del corpo del costruttore dopo il costruttore della superclasse è stata completata l'esecuzione. Così, mentre il costruttore XMPPSubservice
è in esecuzione, mObj
è nullo -. Quindi si chiama un metodo virtuale dal costruttore, e l'override in esegue PublicDataService
Morale: non chiamare metodi virtuali da costruttori, a meno che davvero è necessario, nel qual caso si dovrebbe documentare loro realmente cura. (Molto raramente è utile, ma si dovrebbe cercare è difficile evitarlo.) In pratica significa che si finisce con una chiamata su un oggetto potenzialmente parzialmente inizializzato, che è quello che sta succedendo qui.
Altri suggerimenti
Ho provato la seguente utilizzando implementazioni stub di oggetti in una VM Java.
public static void main(String[] args) {
Context context = new Context();
PublicDataSubservice pds = new PublicDataSubservice(context);
XMPPService service = new XMPPService();
pds.onServiceInstance(service);
}
Non NullPointerException
.
mi sto perdendo qualcosa? Immagino che onServiceInstance
deve effettivamente ottenere chiamato a seguito di context.getService(intent)
?