Java Auto Cast di un oggetto


Mi chiedo se c'è un modo per lanciare automaticamente un oggetto su un tipo memorizzando il tipo di classe insieme all'oggetto? Pensavo che questo fosse possibile con Java, ma forse no.

Per esempio:

class StorageItem
{
    private int itemcount;

    StorageItem(int itemcount)
    {
        this.itemcount = itemcount;
    }

    int getItemCount()
    {
        return itemcount;
    }
}

class Storage
{
    private Class clazz;

    private Object value;

    public Storage(Class clazz, Object value)
    {
        this.clazz = clazz;
        this.value = value;
    }

    //Is there a way such a call can be created to automatically cast 
    //the object to the class type and return that cast type in a 
    //generic way. The idea being that Storage knows what it should
    //already be cast to. Is this possible?
    public T getValue()
    {
        return clazz.cast(value);
    }
}

Un esempio di utilizzo:

public static void main(String[] args)
{
    //Create storage item
    Storage storage = new Storage(StorageItem.class, new StorageItem(1234));

    //The call to getValue() will automatically cast to the Class passed
    //into Storage.
    int itemcount = storage.getValue().getItemCount(); //returns 1234
}

Ovviamente la chiamata getValue() in Storage è una chiamata pseudocodice, ma è solo lì per fornire l'idea di cosa mi piacerebbe fare.

C'è comunque una chiamata getValue () che trasmetterà automaticamente alla classe digitata memorizzata nel Classe di archiviazione. Ancora una volta, l'idea è che la classe di archiviazione sappia a cosa dovrebbe essere lanciata. O c'è comunque questo può essere fatto a tutti?

StorageItem è solo un semplice esempio. Qui, memorizza solo un int a scopo di discussione. Tuttavia, potrebbe essere più complesso.

Un altro esempio di utilizzo, sarebbe la memorizzazione dell'oggetto di archiviazione in un elenco.

List<Storage> row = new ArrayList<Storage>();
row.add(new Storage(StorageItem.class, 1234));
row.add(new Storage(String.class, "Jason"));
row.add(new Storage(Integer.class, 30));
row.add(new Storage(Double.class, 12.7));

Quindi, è possibile accedervi nel modo seguente.

//calls StorageItem's getItemCount() method
row.get(0).getValue().getItemCount(); //returns 1234

//calls String's length() method
row.get(1).getValue().length(); //returns 5

//calls Integer's intValue() method
row.get(2).getValue().intValue(); 

//calls Integer's doubleValue() method
row.get(3).getValue().doubleValue(); 

Se getValue () restituisse sempre e solo un Oggetto, avrei per lanciare sempre manualmente l'oggetto specifico. Invece, se riesco a memorizzare la classe cast all'interno dell'oggetto di archiviazione, lo storage ha informazioni sufficienti per sapere su cosa lanciare automaticamente l'oggetto nella chiamata getValue ().

Se questo è fattibile in Java è la risposta alla domanda che sto cercando. E se sì, come?

Author: JasonF, 2016-05-21

2 answers

Questo farebbe il trucco? Molto meno hacking è richiesto:

class Storage<T> {

    private T value;

    public Storage(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}
 6
Author: Balázs Édes, 2016-05-21 19:15:27

Non vedo davvero il problema che stai cercando di risolvere qui. La risposta di @ bali182 ti dà un modo "generico" per memorizzare un riferimento, ma memorizzare il riferimento stesso è semplicemente più semplice.

Considera cosa succede se inserisci due istanze Storage, contenenti riferimenti tipizzati in modo diverso, in una raccolta:

List<Storage<SOMETHING>> storages = new ArrayList<>();
storages.add(new Storage<String>("Hello"));
storages.add(new Storage<Integer>(1));

Quindi: cos'è SOMETHING? Bene, deve essere ?, poiché questo è l'unico tipo che soddisfa entrambi gli elementi.

Ora, quando si scorre l'elenco per recuperarli, devi affrontarli come Object:

for (Storage<?> storage : storages) {
  Object object = storage.getValue();
  // ...
}

Perché in generale non si sa quale sia il tipo di riferimento memorizzato per un dato elemento. Il tipo concreto di object sarà il tipo concreto dell'elemento - String e Integer, per l'elenco sopra - ma non è possibile utilizzare questi diversi tipi senza utilizzare alcuni mezzi per rilevare quel tipo (ad esempio if (object instanceof String)).

Sarebbe stato più semplice se i riferimenti fossero stati memorizzati direttamente nel elenco:

List<Object> objects = new ArrayList<>();
storages.add("Hello");
storages.add(1;
for (Object object : objects) {
  // ...
}

Dovresti comunque fare qualcosa per rilevare il tipo concreto di oggetto; lo stai facendo senza il livello aggiuntivo di indirezione.


Sebbene l'esempio precedente sia per tipi non correlati, è ancora più semplice farlo con i riferimenti diretti se sono dello stesso tipo:

List<String> objects = Arrays.asList("Hello", "World");
for (String object : objects) {
  // ...
}

Ora non è necessario fare nulla per conoscere il tipo concreto (tu potresti , se gli elementi fossero di una classe non finale e volessi gestirne alcuni sottoclassi appositamente), ma hai comunque evitato di dover dereferenziare Storage.getValue() per ottenere un valore che potresti avere direttamente.

 1
Author: Andy Turner, 2016-05-21 20:07:34