Kako čitati vrijednost privatnog polja iz druge klase u Javi?

Imam loše dizajniranu klasu u JAR -u treće strane i moram pristupiti jednoj od njegovih privatnih . Na primjer, zašto moram odabrati privatno polje, je li to potrebno?

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

Kako mogu iskoristiti refleksiju kako bih dobio vrijednost stuffIWant ?

405
28 июля '09 в 22:20 2009-07-28 22:20 Frank Krueger pitao je 28. srpnja u 22:20 sati
@ 11 odgovora

Da biste pristupili privatnim poljima, morate ih dobiti iz deklariranih razreda i učiniti ih dostupnim:

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

EDIT : kako je aperkins komentirao kako pristupiti polju, postavljajući ga i dostupnim i dobivanjem vrijednosti, svatko će baciti Exception s, iako su samo provjerene iznimke o kojima se trebate sjetiti komentirane gore.

NoSuchFieldException će se odabrati ako zatražite polje po imenu koje ne odgovara deklariranom polju.

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

IllegalAccessException bit će odabran ako polje nije bilo dostupno (na primjer, ako je zatvoreno i nije dostupno kroz nedostaje f.setAccessible(true) niz.

RuntimeException , koji se može resetirati, bilo SecurityException (ako JVM SecurityManager ne dopušta promjenu dostupnosti polja) ili IllegalArgumentException s, ako pokušate pristupiti polju objekta, a ne vrsti razreda polja:

 f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type 
572
28 июля '09 в 22:22 2009-07-28 22:22 odgovor je dao oxbow_lakes 28. srpnja '09 u 10:22 2009-07-28 22:22

Pokušajte Apache FieldUtils commons->border=0

 FieldUtils.readField(object, fieldName, true); 
117
25 апр. odgovor je dan yegor256 25. travnja 2014-04-25 10:40 '14 u 10:40 2014-04-25 10:40

Refleksija nije jedini način za rješavanje vašeg problema (pristup privatnoj funkcionalnosti / ponašanju klase / komponente)

Alternativno rješenje je izdvojiti klasu iz .jar, dekompilirati pomoću (recimo) Jode ili Jad , promijeniti polje (ili dodati dodatak) i ponovno ga prevesti protiv izvornog .jar. Zatim stavite novu .class ispred .jar u classpath ili je zalijepite u .jar . (Uslužni program jar omogućuje vam izdvajanje i ponovno umetanje u postojeći .jar.)

Kao što je navedeno u nastavku, ovo rješava širi problem pristupa / promjene privatnog stanja, umjesto jednostavnog pristupa / promjene polja.

To, naravno, zahtijeva da se ne potpisuje.

23
29 июля '09 в 0:16 2009-07-29 00:16 je odgovorio Brianu Agnewu 29. srpnja 2009. u 0:16 2009-07-29 00:16

Još jedna mogućnost koja još nije spomenuta: koristite Groovy . Groovy vam omogućuje da se odnosi na varijable privatne instance kao nuspojava dizajna jezika. Bez obzira imate li dobitnika za polje, možete ga jednostavno koristiti

 def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant() 
16
19 янв. odgovor dala lucas 19. siječnja 2010-01-19 17:48 '10 u 17:48 2010-01-19 17:48

Koristeći Reflection u Javi , možete pristupiti svim private/public poljima i metodama jedne klase u drugu. Ali prema Oracleu , preporučili su nedostatke u odjeljku:

"Budući da refleksija dopušta kodu za izvođenje operacija koje bi bile nezakonite u nereflektivnom kodu, na primjer, pristup privatnim poljima i metodama, korištenje refleksije može dovesti do neočekivanih nuspojava, što može dovesti do disfunkcije koda i može dovesti do oštećenja prenosivosti. prekida apstrakcije i stoga može promijeniti ponašanje prilikom ažuriranja platforme "

Evo kodnih veza koje prikazuju osnovne koncepte refleksije.

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.> 

Nadam se da ovo pomaže.

7
24 сент. Odgovor daje Simmant 24. rujna. 2014-09-24 09:54 '14 u 9:54 2014-09-24 09:54

Kao oxbox_lakes spominje, možete koristiti refleksiju za zaobilaženje ograničenja pristupa (pod uvjetom da vam SecurityManager omogućuje).

Međutim, ako je ova klasa tako loše osmišljena da vas prisiljava na takvo sjeckanje, možda ćete morati potražiti alternativu. Naravno, ovaj mali hak može vas spasiti za nekoliko sati, ali koliko će vas to stajati na putu?

5
28 июля '09 в 22:36 2009-07-28 22:36 Odgovor je dao Laurence Gonsalves 28. srpnja '09 u 22:36 2009-07-28 22:36

Koristite okruženje za optimizaciju Java čarobnjaka za izravno mijenjanje bajtnog koda. http://www.sable.mcgill.ca/soot/

Jug je u potpunosti napisan u Javi i radi s novijim verzijama Jave.

4
19 янв. odgovor od pcpratts 19 sij 2010-01-19 17:42 '10 u 17:42 2010-01-19 17:42

Morate učiniti sljedeće:

 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); } 
2
13 февр. Luke Hutchison objavio je 13. veljače 2017-02-13 12:08 '17 u 12:08 2017-02-13 12:08

Samo dodatna napomena o refleksiji: u nekim posebnim slučajevima promatrala sam kada nekoliko klasa s istim nazivom postoje u različitim paketima, ova refleksija, korištena u gornjem odgovoru, možda neće odabrati točnu klasu iz objekta. Stoga, ako znate što je objekt package.class, bolje je pristupiti njegovim privatnim vrijednostima polja kako slijedi:

 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); 

(Ovo je primjer klase koji nije radio za mene.)

1
09 сент. odgovor je dan xtof54 09 sep . 2015-09-09 10:59 '15 u 10:59 2015-09-09 10:59

Možete koristiti manifold @JailBreak za izravnu Java refleksiju:

 @JailBreak Foo foo = new Foo(); foo.stuffIWant = "123; public class Foo { private String stuffIWant; } 

@JailBreak otključava lokalnu varijablu foo u kompajleru za izravan pristup svim članovima u Foo hijerarhiji.

Slično tome, možete upotrijebiti metodu proširenja za jailbreak () za jednokratnu upotrebu:

 foo.jailbreak().stuffIWant = "123"; 

Pomoću metode jailbreak() možete pristupiti bilo kojem članu u Foo hijerarhiji.

U oba slučaja, kompajler vam omogućuje siguran pristup polju, kao da je otvoreno polje, dok Manifold generira efektivni kod refleksije za vas iznutra.

Saznajte više o kolektoru .

0
09 дек. Odgovor daje Scott McKinney 9. prosinca. 2018-12-09 07:22 '18 u 7:22 sati 2018-12-09 07:22

Kada koristite Spring ReflectionTestUtils pruža neke praktične alate koji ovdje pomažu uz minimalan napor. Opisan je kao "za upotrebu u pojedinačnim i integracijskim testnim scenarijima." Postoji i slična klasa ReflectionUtils , ali to je opisano kao "Samo za internu uporabu" - pogledajte ovaj odgovor za tumačenje toga što to znači.

Da biste pogledali objavljeni primjer:

 Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant"); 
0
23 окт. Odgovor dao Steve Chambers 23. listopada 2017-10-23 16:25 '17 u 16:25 2017-10-23 16:25