Como ler o valor de um campo privado de uma class diferente em Java?

Eu tenho uma class mal projetada em um JAR terceiros e preciso acessar um de seus campos privados . Por exemplo, por que eu preciso escolher um campo privado, é necessário?

 class IWasDesignedPoorly { private Hashtable stuffIWant; } IWasDesignedPoorly obj = ...; 

Como posso usar a reflection para obter o valor de stuffIWant ?

Para acessar campos particulares, você precisa obtê-los dos campos declarados da class e torná-los acessíveis:

 Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException f.setAccessible(true); Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException 

EDIT : como foi comentado por aperkins , ambos acessando o campo, definindo-o como acessível e recuperando o valor todos lançarão Exception , embora as únicas exceções verificadas que você precisa ter em mente sejam comentadas acima.

O NoSuchFieldException seria lançado se você solicitasse um campo por um nome que não correspondesse a um campo declarado.

 obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException 

A IllegalAccessException seria lançada se o campo não estivesse acessível (por exemplo, se fosse particular e não estivesse acessível por meio da falta da linha f.setAccessible(true) .

Os RuntimeException s que podem ser lançados são SecurityException s (se o SecurityManager da JVM não permitir que você altere a acessibilidade de um campo) ou IllegalArgumentException se você tentar acessar o campo em um object que não seja do tipo da class do campo:

 f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type 

Tente FieldUtils do apache commons-lang3:

 FieldUtils.readField(object, fieldName, true); 

Reflexão não é a única maneira de resolver seu problema (que é acessar a funcionalidade / comportamento privado de uma class / componente)

Uma solução alternativa é extrair a class do .jar, descompactá-lo usando (digamos) Jode ou Jad , alterar o campo (ou adicionar um acessador) e recompilá-lo contra o .jar original. Em seguida, coloque a nova class. À frente do .jar no caminho de class ou insira-a novamente no .jar . (o utilitário jar permite que você extraia e reinsira em um .jar existente)

Conforme observado abaixo, isso resolve a questão mais ampla de acessar / alterar o estado privado em vez de simplesmente acessar / alterar um campo.

Isso requer que o .jar não seja assinado, é claro.

Uma outra opção que ainda não foi mencionada: use o Groovy . O Groovy permite acessar variables ​​de instâncias privadas como um efeito colateral do design do idioma. Se você tem ou não um getter para o campo, você pode simplesmente usar

 def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant() 

Usando o Reflection em Java, você pode acessar todos os campos e methods private/public de uma class para outra. Mas, de acordo com a documentação do Oracle nas desvantagens da seção, eles recomendaram que:

“Como a reflection permite que o código execute operações que seriam ilegais em código não reflexivo, como acessar campos e methods privados, o uso da reflection pode resultar em efeitos colaterais inesperados, o que pode tornar o código disfuncional e pode destruir a portabilidade. quebra abstrações e, portanto, pode mudar o comportamento com atualizações da plataforma “

aqui está seguindo código snapts para demonstrar conceitos básicos de reflection

Reflection1.java

 public class Reflection1{ private int i = 10; public void methoda() { System.out.println("method1"); } public void methodb() { System.out.println("method2"); } public void methodc() { System.out.println("method3"); } } 

Reflection2.java

 import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Reflection2{ public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method[] mthd = Reflection1.class.getMethods(); // for axis the methods Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields // Loop for get all the methods in class for(Method mthd1:mthd) { System.out.println("method :"+mthd1.getName()); System.out.println("parametes :"+mthd1.getReturnType()); } // Loop for get all the Field in class for(Field fld1:fld) { fld1.setAccessible(true); System.out.println("field :"+fld1.getName()); System.out.println("type :"+fld1.getType()); System.out.println("value :"+fld1.getInt(new Reflaction1())); } } } 

Espero que ajude.

Como oxbow_lakes menciona, você pode usar a reflection para contornar as restrições de access (supondo que o seu SecurityManager permita).

Dito isto, se esta class é tão mal projetada que faz você recorrer a tal hackeria, talvez você deva procurar uma alternativa. Claro que esse pequeno truque pode estar te poupando algumas horas agora, mas quanto vai te custar no caminho?

Use o framework Soot Java Optimization para modificar diretamente o bytecode. http://www.sable.mcgill.ca/soot/

Soot é completamente escrito em Java e funciona com novas versões do Java.

Você precisa fazer o seguinte:

 private static Field getField(Class cls, String fieldName) { for (Class c = cls; c != null; c = c.getSuperclass()) { try { final Field field = c.getDeclaredField(fieldName); field.setAccessible(true); return field; } catch (final NoSuchFieldException e) { // Try parent } catch (Exception e) { throw new IllegalArgumentException( "Cannot access field " + cls.getName() + "." + fieldName, e); } } throw new IllegalArgumentException( "Cannot find field " + cls.getName() + "." + fieldName); } 

Apenas uma nota adicional sobre reflection: Eu observei em alguns casos especiais, quando existem várias classs com o mesmo nome em pacotes diferentes, aquela reflection como usada na resposta top pode falhar em escolher a class correta do object. Então, se você sabe o que é o package.class do object, então é melhor acessar seus valores de campo privados da seguinte forma:

 org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0); Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver"); f.setAccessible(true); Solver s = (Solver) f.get(ll); 

(Este é o exemplo de class que não estava funcionando para mim)

Se estiver usando o Spring, o ReflectionTestUtils fornece algumas ferramentas úteis que ajudam aqui com o mínimo de esforço. Ele é descrito como “para uso em cenários de teste de unidade e integração” . Há também uma class semelhante chamada ReflectionUtils, mas isso é descrito como “destinado apenas para uso interno” – veja esta resposta para uma interpretação do que isso significa.

Para endereçar o exemplo postado:

 Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");