& # 8220; Dinamica & # 8221; Casting in Java
-
03-07-2019 - |
Domanda
Salve a tutti,
Mi chiedo se ci sono hacker Java che possono darmi un'idea del perché non funziona:
public class Parent {
public Parent copy() {
Parent aCopy = new Parent();
...
return aCopy;
}
}
public class ChildN extends Parent {
...
}
public class Driver {
public static void main(String[] args) {
ChildN orig = new ChildN();
...
ChildN copy = orig.getClass().cast(orig.copy());
}
}
Il codice è abbastanza felice da compilare, ma decide di lanciare ClassCastException in fase di esecuzione D =
Modifica: Whoah, risposte davvero veloci. Grazie ragazzi! Quindi sembra che non sia possibile effettuare il downcast utilizzando questo metodo ... esiste un altro modo per eseguire il downcast in Java? Ho pensato di avere ogni classe ChildN
sovrascrivere copy ()
, ma non ero entusiasta di aggiungere il codice aggiuntivo boilerplate.
Soluzione
(Impossibile aggiungere il codice in un commento, quindi aggiungerò qui)
Riguardo a Cloneable: se stai implementando Cloneable, implementalo come segue; molto più pulito da chiamare ...
public class Foo implements Cloneable {
public Foo clone() {
try {
return (Foo) super.clone();
} catch (CloneNotSupportedException e) {
return null; // can never happen!
}
}
[EDIT: ho visto anche altre persone usare
throw new AssertionError("This should never happen! I'm Cloneable!");
nel blocco catch.]
Altri suggerimenti
È come provare a fare questo:
public Object copy(){
return new Object();
}
E quindi provare a:
String s = ( String ) copy();
La tua classe Parent e ChildN hanno la stessa relazione di Object e String
Per farlo funzionare dovresti fare quanto segue:
public class ChildN extends Parent {
public Parent copy() {
return new ChildN();
}
}
Cioè, sovrascrivi la " copia " metodo e restituisce l'istanza corretta.
Modifica
Come da modifica. Questo è effettivamente possibile. Questo potrebbe essere un modo possibile:
public class Parent {
public Parent copy() {
Parent copy = this.getClass().newInstance();
//...
return copy;
}
}
In questo modo non è necessario ignorare " copia " metodo in ogni sottoclasse. Questo è il modello di progettazione del prototipo.
Comunque usando questa implementazione dovresti essere consapevole di due eccezioni verificate. Ecco il programma completo che viene compilato ed eseguito senza problemi.
public class Parent {
public Parent copy() throws InstantiationException, IllegalAccessException {
Parent copy = this.getClass().newInstance();
//...
return copy;
}
}
class ChildN extends Parent {}
class Driver {
public static void main(String[] args) throws InstantiationException , IllegalAccessException {
ChildN orig = new ChildN();
ChildN copy = orig.getClass().cast(orig.copy());
System.out.println( "Greetings from : " + copy );
}
}
Il cast sta effettivamente cercando di farlo:
ChildN copy = (ChildN) orig.copy();
(Sta lavorando al cast per esibirsi al momento dell'esecuzione, ma è quello che sarà perché orig.getClass ()
sarà ChildN.class
) Tuttavia, < code> orig.copy () non restituisce un'istanza di ChildN, restituisce un'istanza di Parent
, quindi non può essere trasmessa a ChildN
.
Se ChildN non sovrascrive copy () per restituire un'istanza di ChildN, allora si sta tentando di eseguire il downcast di un oggetto di tipo parent per digitare ChildN
java.lang.Class # cast (Object) genera ClassCastException se Class # isInstance () restituisce false. Dal javadoc per quel metodo:
Determina se l'oggetto specificato è assegnazione compatibile con l'oggetto rappresentato da questa classe. Questo metodo è l'equivalente dinamico di Java istanza della lingua dell'operatore ... In particolare, se questo L'oggetto
Class
rappresenta a classe dichiarata, questo metodo restituiscetrue
se specificato L'argomentoObject
è un istanza della classe rappresentata (o di una delle sue sottoclassi); ritornafalse
altrimenti.
Poiché Parent non è una sottoclasse di child, isInstance () restituisce false, quindi cast () genera l'eccezione. Questo può violare il principio del minimo stupore, ma funziona nel modo in cui è documentato: cast () può solo eseguire l'upgrade, non il downcast.
Potrebbe essere che tu voglia semplicemente una copia / clone del tuo oggetto.
In tal caso, implementare l'interfaccia Cloneable e sovrascrivere clone () se necessario.
public class Parent implement Cloneable {
public Object clone() throws CloneNotSupportedException {
Parent aCopy = (Parent) super.clone();
...
return aCopy;
}
}
public class ChildN extends Parent {
...
}
public class Driver {
public static void main(String[] args) {
ChildN orig = new ChildN();
...
ChildN copy = orig.getClass().cast(orig.clone());
}
}
Questo fa esattamente ciò che " copy () " metodo ha provato a fare in modo Java.
(Sì, potresti anche fare fantasiosi giochi di introspezione, ma quei metodi falliscono o diventano brutti non appena non hai un costruttore predefinito pubblico. Il clone funziona in ogni caso, indipendentemente dai costruttori che hai.)
Il motivo del downcasting non funziona perché, quando si esegue il cast di un oggetto parent su un tipo child, non è possibile invocare i metodi del tipo child sull'oggetto parent. Ma funziona nell'altro modo ...