Visualizzazione dei risultati da 1 a 5 su 5
  1. #1

    [Reflection] Eccezione nel settare un valore di un field

    Salve a tutti.

    Non ho lavorato molto con la Reflection e ora mi trovo a scrivere un metodo che debba impostare il valore di un field con il valore di una oggetto di tipo sconosciuto (istanza di Object).

    Ho scritto un programmino di esempio per mostrare dove non riesco a trovare soluzione. Elenco alcuni punti fondamentali:
    - ho una classe con 4 fields di cui 2 int e 2 Integer.
    - alla linea 27 imposto il nome del field che voglio modificare
    - se imposto field1 o field2 (entrambi di tipo primitivo int) non c'é problema. Viene eseguito il 'case "int"' del successivo switch
    - se imposto field3 o field4 (di tipo Integer) viene eseguito il 'default' dello switch e mi dá eccezione alla linea 56 "Cannot cast java.lang.String to java.lang.Integer"

    Qualcuno sa aiutarmi? Grazie in anticipo.
    codice:
    import java.lang.reflect.Field;
    
    public class TestClass {
        public int field1 = 1;
        public int field2 = 2;
        public Integer field3 = new Integer(3);
        public Integer field4 = new Integer("4");
        
        public static void main(String[] args) {
            try {
                TestClass object = new TestClass();
                Object newValue = "9999999";
                Field[] fields = object.getClass().getDeclaredFields();
                System.out.println("Execution 1");
                for (Field field : fields) {
                    System.out.print("Field name: ");
                    System.out.print(field.getName());
                    System.out.print(" - Type: ");
                    System.out.print(field.getType().getName());
                    System.out.print(" - Value: ");
                    System.out.print(field.get(object).toString());
                    Object value = field.get(object);
                    System.out.print(" - Value type: ");
                    System.out.print(value.getClass().getName());
                    System.out.println();
                }
                Field f = object.getClass().getField("field3");
                f.setAccessible(true);
                switch (f.getType().getName()) {
                    case "boolean":
                        f.setBoolean(object, Boolean.parseBoolean(newValue.toString()));
                        break;
                    case "byte":
                        f.setByte(object, (newValue.toString().getBytes()[0]));
                        break;
                    case "char":
                        f.setChar(object, (newValue.toString().charAt(0)));
                        break;
                    case "double":
                        f.setDouble(object, Double.parseDouble(newValue.toString()));
                        break;
                    case "float":
                        f.setFloat(object, Float.parseFloat(newValue.toString()));
                        break;
                    case "int":
                        f.setInt(object, Integer.parseInt(newValue.toString()));
                        break;
                    case "long":
                        f.setLong(object, Long.parseLong(newValue.toString()));
                        break;
                    case "short":
                        f.setShort(object, Short.parseShort(newValue.toString()));
                        break;
                    default:
                        newValue = f.getType().cast(newValue);
                        f.set(object, newValue);
                        break;
                }
                
                System.out.println("Execution 2");
                for (Field field : fields) {
                    System.out.print("Field name: ");
                    System.out.print(field.getName());
                    System.out.print(" - Type: ");
                    System.out.print(field.getType().getName());
                    System.out.print(" - Value: ");
                    System.out.print(field.get(object).toString());
                    Object value = field.get(object);
                    System.out.print(" - Value type: ");
                    System.out.print(value.getClass().getName());
                    System.out.println();
                }
            } catch (NoSuchFieldException ex) {
                System.out.println("NoSuchFieldException: " + ex.getMessage());
            } catch (SecurityException ex) {
                System.out.println("SecurityException: " + ex.getMessage());
            } catch (Throwable ex) {
                System.out.println("Throwable: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320
    C'è un evidente errore nel tuo codice, deducibile direttamente dal tuo ragionamento.
    Dici che hai 4 campi: due di tipo "int" e due di tipo "Integer".

    Ora, andando di reflection, arriverai al primo dei due campi di tipo Integer.

    Ottieni tale campo e fai questa operazione:

    codice:
    newValue = f.getType().cast(newValue);

    Cioè, stai assegnando alla variabile "newValue" il valore del cast... il problema di fondo è che con quell'istruzione tu stai cercando di castare l'attuale valore di "newValue" (che è uno String) in un Integer. E questo non è possibile (se sai cos'è un cast).
    Osserva bene cos'hai scritto nella dichiarazione (+ inizializzazione) della variabile "newValue":

    codice:
    Object newValue = "9999999";   // <-- E' un oggetto di tipo String

    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3
    Grazie per la risposta Lele e scusa il ritardo.

    Aggiungo qualcosa:
    mi ha tratto in inganno il fatto che il metodo Field.set(Object o1, Object o2) aspettasse 2 parametri di tipo Object, come a dire "dammi qualsiasi cosa, ci penso io". In realtá conta solo il fatto che il secondo parametro possa essere effettivamente castato al tipo del primo e ció, se non erro, é possibile solo quando il secondo parametro é della stessa classe del primo, oppure quando é una sua sottoclasse o quando ne implementa l'interfaccia.

    In particolare, String puó essere castato a Object ma non il contrario. In realtá davo per scontato che per me la stringa "24253" é il numero di tipo Integer di tale valore. In realtá ci sono troppe variabili da considerare e specificare, nell'assegnare il valore numerico ad una stringa, come ad es.:
    - chi mi dice che quel valore é in base 10?
    - chi mi dice che quella stringa non é solo una concatenazione di simboli e non ha alcun valore numerico?

    Agli occhi di un umano l'associazione numero -> valore (in base 10) é automatica, a quelli di un computer no. Peró..... perché a questo punto non inserire un cast intelligente (o stupido, dipende dai punti di vista) che ci permette automaticamente di castare stringhe ad oggetti di altri tipi, cosí come un umano di primo acchitto farebbe?

    Ho scritto qualcosa a tal proposito, che utilizzo nel mio programma. Magari puó interessare a qualcuno...
    codice:
    public static Object castValueToType(Class<?> type, Object valueObject) throws Exception {
        if (type == null) throw new IllegalArgumentException("Type cannot be null");
        if (!(valueObject instanceof String)) throw new UnsupportedOperationException("Casting of not String values is not supported");
        String valueString = valueObject.toString();
        String fieldTypeName = type.getName();
        try {
                 if (fieldTypeName.equals("boolean")) return Boolean.parseBoolean(valueString);
            else if (fieldTypeName.equals("byte")) return (valueString.getBytes()[0]);
            else if (fieldTypeName.equals("char")) return (valueString.charAt(0));
            else if (fieldTypeName.equals("double")) return Double.parseDouble(valueString);
            else if (fieldTypeName.equals("float")) return Float.parseFloat(valueString);
            else if (fieldTypeName.equals("int")) return Integer.parseInt(valueString);
            else if (fieldTypeName.equals("long")) return Long.parseLong(valueString);
            else if (fieldTypeName.equals("short")) return Short.parseShort(valueString);
            else if (Boolean.class.equals(type)) return Boolean.valueOf(valueString);
            else if (Byte.class.equals(type)) return Byte.valueOf(valueString);
            else if (Character.class.equals(type)) return valueString.charAt(0);
            else if (Double.class.equals(type)) return Double.valueOf(valueString);
            else if (Float.class.equals(type)) return Float.valueOf(valueString);
            else if (Integer.class.equals(type)) return Integer.valueOf(valueString);
            else if (Long.class.equals(type)) return Long.valueOf(valueString);
            else if (Short.class.equals(type)) return Short.valueOf(valueString);
            else if (String.class.equals(type)) return valueString;
            else if (java.math.BigDecimal.class.equals(type)) return java.math.BigDecimal.valueOf(Double.valueOf(valueString));
            else if (java.math.BigInteger.class.equals(type)) return java.math.BigDecimal.valueOf(Integer.valueOf(valueString));
            else if (java.sql.Date.class.equals(type)) return java.sql.Date.valueOf(valueString);
            else if (java.sql.Time.class.equals(type)) return java.sql.Time.valueOf(valueString);
            else if (java.sql.Timestamp.class.equals(type)) return java.sql.Timestamp.valueOf(valueString);
            else if (java.util.Date.class.equals(type)) return java.text.DateFormat.getDateInstance().parse(valueString);
            else throw new IllegalArgumentException("Parameter not recognized");
        } catch (Throwable ex) {
            String message = "The value cannot be converted to the field type." + System.lineSeparator();
            message += "Value: " + valueString + System.lineSeparator();
            message += "Field type: " + fieldTypeName + System.lineSeparator();
            message += "Exception message: " + ex.getMessage();
            throw new Exception(message);
        }
    }
    Ultima modifica di VegetaSSJ5; 30-06-2015 a 18:47

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Quote Originariamente inviata da VegetaSSJ5 Visualizza il messaggio
    mi ha tratto in inganno il fatto che il metodo Field.set(Object o1, Object o2) aspettasse 2 parametri di tipo Object, come a dire "dammi qualsiasi cosa, ci penso io".
    Il primo argomento è: o il riferimento ad un oggetto (se il campo è di istanza) oppure è ignorato, può essere null (se il campo è di classe).
    Il secondo argomento non può essere "qualunque" cosa arbitraria. Il limite è dato dal campo e la reflection non prevede conversioni "particolari" ma solo quelle lecite nel senso del linguaggio Java. Cioè al massimo il widening (se il campo è Number, posso passare un Integer ... non un String).
    In più, visto che si passa sempre un oggetto, il unwrapping a primitivo è automatico. Se il campo è un long primitivo, posso passare un java.lang.Byte (Byte ---unboxing---> byte ---widening---> long)

    Quote Originariamente inviata da VegetaSSJ5 Visualizza il messaggio
    perché a questo punto non inserire un cast intelligente (o stupido, dipende dai punti di vista) che ci permette automaticamente di castare stringhe ad oggetti di altri tipi, cosí come un umano di primo acchitto farebbe?
    Deduco che potresti essere interessato alla Apache Commons BeanUtils

    Quote Originariamente inviata da VegetaSSJ5 Visualizza il messaggio
    Ho scritto qualcosa a tal proposito, che utilizzo nel mio programma. Magari puó interessare a qualcuno...
    Se lo fai per diletto o "didattica" .... ok, ci sta.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  5. #5
    Grazie Andbin anche per il suggerimento delle Commons Beanutils.

    L'esempio che ho postato non é un esercizio didattico ma un metodo che uso in un piccolo programma e che mi aiuta a fare quanto descritto nel primo post.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.