Kaip skaityti privačios srities vertę iš kitos klasės Java?

Turiu blogai suprojektuotą klasę trečiosios šalies JAR , ir turiu prieiti prie vieno iš jo privačių . Pavyzdžiui, kodėl man reikia pasirinkti privatų lauką, ar tai būtina?

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

Kaip aš galiu naudoti atspindžius, kad gautumėte stuffIWant vertę?

405
28 июля '09 в 22:20 2009-07-28 22:20 Frank Krueger paklausė liepos 28 d., 09:20 val. 2009-07-28 22:20
@ 11 atsakymų

Jei norite patekti į privačius laukus, turite juos gauti iš deklaruotų klasių ir tada juos pateikti:

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

Redaguoti: kaip aperkins komentavo, kaip pasiekti lauką, nustatydamas, kad jis yra prieinamas, ir gauti vertę, visi išmesti Exception s, nors tik patikrintos išimtys, kurių turėtumėte prisiminti, apie kurias komentuojama aukščiau.

NoSuchFieldException bus pasirinktas, jei paprašysite lauko pagal pavadinimą, kuris neatitinka deklaruoto lauko.

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

IllegalAccessException bus pasirinktas, jei laukas nebus pasiekiamas (pvz., Jei jis uždarytas ir nepasiekiamas per trūkstamą f.setAccessible(true) eilutę).

RuntimeException , kurią galima iš naujo nustatyti, „ SecurityException (jei JVM SecurityManager neleidžia pakeisti lauko prieinamumo) arba „ IllegalArgumentException s, jei bandote pasiekti objekto lauką, o ne lauko klasės tipą:

 f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type 
572
28 июля '09 в 22:22 2009-07-28 22:22 atsakymas duotas oxbow_lakes liepos 28 d., 09:22, 2009-07-28 22:22

Išbandykite „apache FieldUtils commons->border=0

 FieldUtils.readField(object, fieldName, true); 
117
25 апр. atsakymas pateikiamas yegor256 25 Bal 2014-04-25 10:40 '14, 10:40 2014-04-25 10:40

Atspindėjimas nėra vienintelis būdas išspręsti jūsų problemą (prieiti prie privataus funkcionalumo / klasės / komponento elgesio)

Alternatyvus sprendimas yra išgauti klasę iš .jar, jį dekompiliuoti naudojant (pasakyti) Jode arba Jad , pakeisti lauką (arba pridėti priedą) ir perskaičiuoti jį prieš originalų .jar. Tada įdėkite naują .class į .jar klasėje, arba įklijuokite jį į .jar . („Jr“ programa leidžia išgauti ir įterpti į esamą .jar.)

Kaip nurodyta toliau, tai išsprendžia platesnę privataus valstybės prieigos / keitimo problemą, o ne tiesiog prieiti prie lauko.

Tam, žinoma, nereikia pasirašyti .jar .

23
29 июля '09 в 0:16 2009-07-29 00:16 atsakė Brian Agnew'ui liepos 29 d., 09:16, 2009-07-29 00:16

Kitas variantas, kuris dar nebuvo paminėtas: naudokite Groovy . „Groovy“ leidžia nurodyti privačių pavyzdžių kintamuosius kaip kalbos dizaino šalutinį poveikį. Nepriklausomai nuo to, ar turite lauką, galite tiesiog naudoti

 def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant() 
16
19 янв. atsakymas, kurį pateikė lucas Jan 19 2010-01-19 17:48 '10, 17:48, 2010-01-19 17:48

Naudodamiesi „ Reflection“ programoje „Java“ , galite prieiti prie visų klasių private/public sričių ir metodų į kitą. Tačiau, anot „ Oracle“ , jie rekomendavo trūkumus skyriuje:

„Kadangi atspindys leidžia kodui atlikti operacijas, kurios būtų neteisėtos neatspindinčiame kode, pavyzdžiui, prieiga prie privačių laukų ir metodų, naudojant atspindį, gali sukelti netikėtus šalutinius poveikius, kurie gali sukelti kodo disfunkciją ir gali sukelti sugadinimo žalą. nutraukia abstrakcijas ir todėl gali pakeisti elgesį atnaujinant platformą “

Čia pateikiami kodo sujungimai, skirti pagrindinėms refleksijos sąvokoms .

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

Tikiuosi, kad tai padės.

7
24 сент. Atsakymą pateikė Simmant rugsėjo 24 d. 2014-09-24 09:54 '14 at 9:54 2014-09-24 09:54

Kaip nurodo oxbow_lakes, galite naudoti refleksiją, kad apeitumėte prieigos apribojimus (su sąlyga, kad jūsų SecurityManager leidžia jums).

Tačiau, jei ši klasė yra tokia prastai suprojektuota, kad verčia jus pasinaudoti tokiu įsilaužimu, jums gali tekti ieškoti alternatyvos. Žinoma, šis mažai įsilaužimas gali jums sutaupyti per kelias valandas, bet kiek kainuos jums kelyje?

5
28 июля '09 в 22:36 2009-07-28 22:36 Atsakymą pateikė Laurence Gonsalves liepos 28 d., 09:36, 10:36 2009-07-28 22:36

Naudokite „Soot Java Optimization“ aplinką, jei norite tiesiogiai pakeisti baitekodą. http://www.sable.mcgill.ca/soot/

Pietų yra visiškai parašyta „Java“ ir veikia su naujesnėmis „Java“ versijomis.

4
19 янв. atsakymą pateikė pcpratts 19 sausis 2010-01-19 17:42 '10, 17:42, 2010-01-19 17:42

Turite atlikti šiuos veiksmus:

 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 paskelbė vasario 13 dieną 2017-02-13 12:08 '17, 12:08 2017-02-13 12:08

Tik papildoma pastaba apie atspindį: kai kuriais ypatingais atvejais pastebėjau, kad kai kuriose pakuotėse egzistuoja keletas tos pačios rūšies klasių, šis atspindys, naudojamas viršutiniame atsakyme, negali pasirinkti tinkamos klasės iš objekto. Todėl, jei žinote, kas yra objekto paketas, geriau pasiekti privataus lauko reikšmes taip:

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

(Tai yra pavyzdinė klasė, kuri man neveikė.)

1
09 сент. atsakymas pateikiamas xtof54 09 sep . 2015-09-09 10:59 '15 , 10:59 2015-09-09 10:59

Galite naudoti „ Manifold“ @JailBreak tiesioginiam, saugiam „Java“ atspindžiui:

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

@JailBreak atveria vietinį kintamąjį foo kompiliatoriuje, kad galėtumėte tiesiogiai pasiekti visus narius Foo hierarchijoje.

Panašiai galite naudoti vienkartinio jailbreak () plėtinio metodą:

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

Naudojant jailbreak() metodą, galite pasiekti bet kurį „ Foo hierarchijos narį.

Abiem atvejais kompiliatorius leidžia jums saugiai pasiekti lauką, tarsi jis būtų atviras laukas, o „Manifold“ sukuria efektyvų atspindžio kodą iš jūsų.

Sužinokite daugiau apie kolektorių .

0
09 дек. Atsakymą pateikė Scott McKinney 09 dec. 2018-12-09 07:22 '18, 07:22 am 2018-12-09 07:22

Naudojant „Spring ReflectionTestUtils“ yra keletas patogių įrankių, kurie padeda čia su minimaliomis pastangomis. Jis apibūdinamas kaip „skirtas naudoti vienkartiniuose ir integravimo bandymų scenarijuose“. Taip pat yra panašių klasių ReflectionUtils , tačiau tai apibūdinama kaip „Tik vidiniam naudojimui“ - žiūrėkite šį atsakymą, kad būtų aiškinama, ką tai reiškia.

Jei norite remtis paskelbtu pavyzdžiu:

 Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant"); 
0
23 окт. Steve Chambers atsakymas, pateiktas spalio 23 d 2017-10-23 16:25 '17, 16:25 pm 2017-10-23 16:25