Qual é a diferença entre getFields e getDeclaredFields na reflexão de Java

194

Estou um pouco confuso sobre a diferença entre o getFieldsmétodo e o getDeclaredFieldsmétodo ao usar a reflexão em Java.

Eu li que getDeclaredFieldslhe dá acesso a todos os campos da classe e que getFieldsapenas retorna campos públicos. Se for esse o caso, por que você não usaria sempre getDeclaredFields?

Alguém pode, por favor, elaborar isso e explicar a diferença entre os dois métodos, e quando / por que você gostaria de usar um sobre o outro?

BlackHatSamurai
fonte
3
getFieldpode obter um campo herdado de uma superclasse, mas getDeclaredFieldnão pode. getDeclaredFieldrestringir-se à classe na qual você chama a função.
user2336315
@ user2336315 isso é correto, porém getFieldnão pode acessar membros privados
Madbreaks

Respostas:

258

getFields ()

Todos os publiccampos em toda a hierarquia de classes.

getDeclaredFields ()

Todos os campos, independentemente de sua acessibilidade, mas apenas para a classe atual, não quaisquer classes base das quais a classe atual possa estar herdando.

Para obter todos os campos na hierarquia, escrevi a seguinte função:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

A exclusiveParentclasse é fornecida para impedir a recuperação de campos Object. Pode ser nullque você queira os Objectcampos.

Para esclarecer, Lists.newArrayListvem da goiaba.

Atualizar

Para sua informação, o código acima é publicado no GitHub no meu projeto LibEx no ReflectionUtils .

John B
fonte
8
Ótima resposta, mas deve-se notar que os campos privados nas superclasses não podem ser usados ​​por instâncias da classe atual para Field#getmétodos semelhantes. Em outras palavras, essa abordagem não permite que a classe atual acesse a interface privada de sua superclasse, da mesma forma que a compilação típica não.
FThompson
4
@Vulcan verdadeira a menos que o código é escrito para usar o reflexo para alterar o escopo via setAccessiblee não há Security Manager no lugar
John B
Ligeiro nit, deve ser "(não importa a acessibilidade)" não "(não importa o escopo)". Todos os campos têm o mesmo escopo, a saber, o corpo da classe .
yshavit
@yshavit Obrigado. Atualizada.
John B
1
Não seria. Como os privatecampos só podem ser acessados ​​através do getDeclaredFieldsqual é específico da classe. Cada campo (mesmo com o mesmo tipo e nome) seria Fieldinstâncias distintas .
John B
7

Como já mencionado, Class.getDeclaredField(String)apenas analisa os campos Classnos quais você o chama.

Se você deseja pesquisar um Fieldna Classhierarquia, pode usar esta função simples:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

Isso é útil para encontrar um privatecampo de uma superclasse, por exemplo. Além disso, se você quiser modificar seu valor, poderá usá-lo assim:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}
IvanRF
fonte
ligeira modificação para ainda jogar erro se não for encontrado em tudotry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens
5

public Field[] getFields() throws SecurityException

Retorna uma matriz contendo objetos de campo que refletem todos os campos públicos acessíveis da classe ou interface representada por esse objeto de classe. Os elementos na matriz retornados não são classificados e não estão em nenhuma ordem específica. Esse método retorna uma matriz de comprimento 0 se a classe ou a interface não tiver campos públicos acessíveis ou se ela representa uma classe de matriz, um tipo primitivo ou nulo.

Especificamente, se esse objeto Class representar uma classe, esse método retornará os campos públicos dessa classe e de todas as suas superclasses. Se esse objeto Class representar uma interface, esse método retornará os campos dessa interface e de todas as suas superinterfaces.

O campo de comprimento implícito para a classe da matriz não é refletido por esse método. O código do usuário deve usar os métodos da classe Array para manipular matrizes.


public Field[] getDeclaredFields() throws SecurityException

Retorna uma matriz de objetos Field refletindo todos os campos declarados pela classe ou interface representada por este objeto Class. Isso inclui campos públicos, protegidos, padrão (pacote) e privados , mas exclui os campos herdados . Os elementos na matriz retornados não são classificados e não estão em nenhuma ordem específica. Esse método retorna uma matriz de comprimento 0 se a classe ou a interface não declarar nenhum campo ou se esse objeto Class representar um tipo primitivo, uma classe de matriz ou nulo.


E se eu precisar de todos os campos de todas as classes pai? É necessário algum código, por exemplo, de https://stackoverflow.com/a/35103361/755804 :

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}
18446744073709551615
fonte