2009-04-09 9 views
30

Wie kann ich auf ein geerbtes geschütztes Feld von einem Objekt durch Reflektion zugreifen?Java-Reflektion - Zugriffsschutzfeld

+2

Die Frage wäre besser, wenn Sie festgestellt was du versucht hast (genau) und was genau passiert ist. –

Antwort

50

Zwei Probleme, mit denen Sie möglicherweise Probleme haben - das Feld ist möglicherweise nicht normal zugänglich (privat) und es befindet sich nicht in der Klasse, die Sie betrachten, sondern irgendwo in der Hierarchie.

So etwas wie dies funktionieren würde, auch mit diesen Fragen:

public class SomeExample { 

    public static void main(String[] args) throws Exception{ 
    Object myObj = new SomeDerivedClass(1234); 
    Class myClass = myObj.getClass(); 
    Field myField = getField(myClass, "value"); 
    myField.setAccessible(true); //required if field is not normally accessible 
    System.out.println("value: " + myField.get(myObj)); 
    } 

    private static Field getField(Class clazz, String fieldName) 
     throws NoSuchFieldException { 
    try { 
     return clazz.getDeclaredField(fieldName); 
    } catch (NoSuchFieldException e) { 
     Class superClass = clazz.getSuperclass(); 
     if (superClass == null) { 
     throw e; 
     } else { 
     return getField(superClass, fieldName); 
     } 
    } 
    } 
} 

class SomeBaseClass { 
    private Integer value; 

    SomeBaseClass(Integer value) { 
    this.value = value; 
    } 
} 

class SomeDerivedClass extends SomeBaseClass { 
    SomeDerivedClass(Integer value) { 
    super(value); 
    } 
} 
+5

Wenn ein Sicherheitsmanager vorhanden ist, würde dies fehlschlagen. Du musst den Aufruf von setAccessible und getDeclaredField in einer PriviledgedAction einpacken und es über java.security.AccessController.doPrivileged laufen lassen (...) – Varkhan

+0

Ich liebe dich von ganzem Herzen –

+0

Dies funktioniert nicht auf Android :(Meine Klasse Baum sieht wie folgt aus A-> B-> C und innerhalb CI kann nicht den Wert eines geschützten Felds erhalten, das in A deklariert wird. Beachten Sie, dass alle drei Klassen sich in verschiedenen Paketen befinden. – Moonwalker

4
field = myclass.getDeclaredField("myname"); 
field.setAccessible(true); 
field.set(myinstance, newvalue); 
+1

Dies funktioniert möglicherweise nicht, wenn ein Sicherheitsmanager die entsprechende Berechtigung blockiert. –

+0

getField (String name) ruft nur die öffentlichen Felder ab. – Moro

+0

@Moro: Danke, ich korrigierte den Code –

0

Meinen Sie vielleicht von einem anderen Objekt eines nicht vertrauenswürdiger Zusammenhang mit einem SecurityManager Satz? Das würde das Typsystem durchbrechen, also nicht. Aus einem gesicherten Kontext können Sie aufrufen, um das Typsystem zu besiegen. Im Idealfall keine Reflexion verwenden.

+0

deklariert wurde "Im Idealfall, verwenden Sie keine Reflexion." Warum? Das OP versucht speziell, Reflektion zu verwenden ... während er nicht sagt warum, gibt es viele legitime Verwendungen für die Reflektion (besonders im Testcode). –

+0

Während es legitime Anwendungen der Reflexion gibt, sind die meisten nicht. Insbesondere wenn Sie die Legacy-Fähigkeit, Java (1.0) Sprachzugriffsregeln zu respektieren, schleichen, brauchen Sie es wahrscheinlich nicht. –

+0

@Tom ... außer Sie versuchen, spezifische JUnit-Testfälle zu schreiben, ohne Zugriffsregeln zu lockern, einfach für den Testfall –

0

Sie etwas tun könnte, wie ...

Class clazz = Class.forName("SuperclassObject"); 

Field fields[] = clazz.getDeclaredFields(); 

for (Field field : fields) { 
    if (field.getName().equals("fieldImLookingFor")) { 
     field.set...() // ... should be the type, eg. setDouble(12.34); 
    } 
} 

Möglicherweise müssen auch die Zugänglichkeit ändern, wie in Maurice Antwort zur Kenntnis genommen.

+0

Gibt ein Array von Field-Objekten zurück, die alle Felder widerspiegeln, die von der Klasse oder der Schnittstelle deklariert wurden, die von diesem Klassenobjekt repräsentiert wird. Es ging nicht durch die Super-Klassen – Moro

+0

Konnten Sie mit dem Problem spezifischer sein? Sie sagen, dass es nur die Felder in Ihrer Unterklasse erhalten hat? Haben Sie sichergestellt, dass die Class.forName() -Methode den Namen der Superklasse übergibt und dass die Barrierefreiheit wie von Maurice vorgeschlagen geändert wurde? –

6

Verwenden Reflexion der Mitglieder der Klasse Instanz zugreifen, sie zugänglich machen und ihre jeweiligen Werte gesetzt. Natürlich müssen Sie den Namen jedes Mitglieds kennen, das Sie ändern möchten, aber ich denke, das wird kein Problem sein.

public class ReflectionUtil { 
    public static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { 
     try { 
      return clazz.getDeclaredField(fieldName); 
     } catch (NoSuchFieldException e) { 
      Class superClass = clazz.getSuperclass(); 
      if (superClass == null) { 
       throw e; 
      } else { 
       return getField(superClass, fieldName); 
      } 
     } 
    } 
    public static void makeAccessible(Field field) { 
     if (!Modifier.isPublic(field.getModifiers()) || 
      !Modifier.isPublic(field.getDeclaringClass().getModifiers())) 
     { 
      field.setAccessible(true); 
     } 
    } 
} 

public class Application { 
    public static void main(String[] args) throws Exception { 
     KalaGameState obj = new KalaGameState(); 
     Field field = ReflectionUtil.getField(obj.getClass(), 'turn'); 
     ReflectionUtil.makeAccessible(field); 
     field.setInt(obj, 666); 
     System.out.println("turn is " + field.get(obj)); 
    } 
} 
1

ich nicht so in mehreren Bibliotheken ziehen wollte habe ich eine reine eine, die für mich gearbeitet. Es ist eine Erweiterung von einem der Methoden aus jweyrich:

import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 
import java.util.Date; 
import java.util.Random; 
import java.util.UUID; 

public abstract class POJOFiller { 

    static final Random random = new Random(); 

    public static void fillObject(Object ob) { 
     Class<? extends Object> clazz = ob.getClass(); 

     do { 
      Field[] fields = clazz.getDeclaredFields(); 
      fillForFields(ob, fields); 

      if (clazz.getSuperclass() == null) { 
       return; 
      } 
      clazz = clazz.getSuperclass(); 

     } while (true); 

    } 

    private static void fillForFields(Object ob, Field[] fields) { 
     for (Field field : fields) { 
      field.setAccessible(true); 

      if(Modifier.isFinal(field.getModifiers())) { 
       continue; 
      } 

      try { 
       field.set(ob, generateRandomValue(field.getType())); 
      } catch (IllegalArgumentException | IllegalAccessException e) { 
       throw new IllegalStateException(e); 
      } 
     } 
    } 

    static Object generateRandomValue(Class<?> fieldType) { 
     if (fieldType.equals(String.class)) { 
      return UUID.randomUUID().toString(); 
     } else if (Date.class.isAssignableFrom(fieldType)) { 
      return new Date(System.currentTimeMillis()); 
     } else if (Number.class.isAssignableFrom(fieldType)) { 
      return random.nextInt(Byte.MAX_VALUE) + 1; 
     } else if (fieldType.equals(Integer.TYPE)) { 
      return random.nextInt(); 
     } else if (fieldType.equals(Long.TYPE)) { 
      return random.nextInt(); 
     } else if (Enum.class.isAssignableFrom(fieldType)) { 
      Object[] enumValues = fieldType.getEnumConstants(); 
      return enumValues[random.nextInt(enumValues.length)]; 
     } else if(fieldType.equals(Integer[].class)) { 
      return new Integer[] {random.nextInt(), random.nextInt()}; 
     } 
     else { 
      throw new IllegalArgumentException("Cannot generate for " + fieldType); 
     } 
    } 

} 
0

Eine gattungsgemäße Hilfsmethode jedes Getter in dieser oder einem übergeordneten Klasse auszuführen.

Angepasst von Marius's Antwort.

public static Object RunGetter(String fieldname, Object o){ 
    Object result = null; 
    boolean found = false; 
    //Search this and all superclasses: 
    for (Class<?> clas = o.getClass(); clas != null; clas = clas.getSuperclass()){ 
     if(found){ 
      break; 
     } 
     //Find the correct method: 
     for (Method method : clas.getDeclaredMethods()){ 
      if(found){ 
       break; 
      } 
      //Method found: 
      if ((method.getName().startsWith("get")) && (method.getName().length() == (fieldname.length() + 3))){ 
       if (method.getName().toLowerCase().endsWith(fieldname.toLowerCase())){        
        try{ 
         result = method.invoke(o); //Invoke Getter: 
         found = true; 
        } catch (IllegalAccessException | InvocationTargetException ex){ 
         Logger.getLogger("").log(Level.SEVERE, "Could not determine method: " + method.getName(), ex); 
        } 
       } 
      } 
     } 
    } 
    return result; 
} 

Hoffentlich ist das nützlich für jemanden.

1

Wenn Sie immer nur das Schutzfeld

Field protectedfield = Myclazz.class.getSuperclass().getDeclaredField("num"); 

Wenn Sie Eclipse sind Ctrl +Raum wird eine Liste von Methoden bringen, wenn Sie ein Typ „“ nach dem Objekt

3

Verwenden Sie FieldUtils.writeField(object, "fieldname", value, true) oder readField(object, "fieldname", true) von Apache Commons lang3.

1

Wenn Sie Spring verwenden, bietet ReflectionTestUtils einige praktische Tools, die hier mit minimalem Aufwand helfen.

Zum Beispiel einen geschützten Feldwert zu erhalten, die ein int sein bekannt ist: In diesem Feld der

int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField"); 

Oder alternativ Wert zu setzen:

ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue);