Java refleksja i ustawienia wszystkich pól

0

Witam mam proste pytanie dla ludzi zaznajomionych z tematem

Ogólnie problem polega na ustawieniu wartości wszystkich pól jakie zawiera dana klasa. Nie mam problemu z ustawieniem jej pól lecz z polami odziedziczonymi .

Używając Springa oraz funkcji SpringReflectionUtils pobrałem sobie wszystkie nazwy pól oraz przyporządkowałem im odpowiednie wartości czyli klucz to field.getName(), a value: to odpowiednia wartość dla tego pola.

następnie tworzę egzemplarz tej klasy :


T object = clazz.newInstance();
//tutaj hashmap loop

Field f = clazz.getDeclaredField(entry.getKey());
f.set(object, entry.getValue());

Działa mi to dobrze tylko problem pojawia się gdy chcę ustawić pole które dana klasa dziedziczy ...

funkcja f.set(Object obj, Object value);
działa dobrze tylko nie radzi sobie z polami które obj dziedziczy po innej klasie. Ja potrzebuję w prosty sposób ustawić wszystkie pola nawet te dziedziczone. Jak mogę to zrobić ??

Zastanawiam się , czy w przypadku mojego problemu nie zastosować dynamicznego proxy ?

1

Zamień może getDeclaredField() na getField().

0

To nie to getDeclaredField() zwraca wszystkie pola (prywatne, protected oraz publiczne) getField nie zwróci Ci pól prywatnych .
Ale to nie jest problemem bo to działa i ustawia mi wszystkie pola tylko nie pola dziedziczone po innej klasie:/

1

To jest problem, bo getDeclaredField() zwraca tylko pola zadeklarowane w tej klasie. Wykorzystaj metodę getSuperclass() i dla niej wywołaj getDeclaredField(). Ewentualnie zrób pętlę po getSuperclass() dopóki nie zwróci java.lang.Object.

0

Dokładnie tak zrobiłem i działa dzięki :)

0
lukasw44 napisał(a):

pobrałem sobie wszystkie nazwy pól oraz przyporządkowałem im odpowiednie wartości czyli klucz to field.getName(), a value: to odpowiednia wartość dla tego pola.

problem główny masz rozwiązany, ale bezpieczniej będzie, jeśli zadam pytanie, a Ty sobie na nie odpowiesz:

wziąłeś pod uwagę, że możesz mieć wiele pól prywatnych o tej samej nazwie? Każda klasa w łańcuchu dziedziczenia może zadeklarować swoje prywatne pole o nazwie "uid".

Przymyśl to sobie już teraz, i przetraw, co z tym fantem rozwiązać planujesz? ;) Bo zwykła mapa "nazwa=>value" Ci się nie sprawdzi, jeśli wyciągasz wszystkie pola z całej hierarchii ;) Jak kiedyś to wyjdzie w praktyce, to możesz pół dnia przesiedzieć myśląc nad tym, co się dzieje. I dopiero pod wieczór wpadniesz na to, że zmieniasz wartość prywatnego pola "uid" nie w tej klasie co trzeba ;)

0

Już nie jestem taki pewien czy Twoje zastrzeżenia są uzasadnione. Załóżmy, że mamy hierarchię klas (od przodka): A => B => C => D, nazwa pewnego pola (np. id) powtarza się. Tworzymy obiekt D d = new D(). Jaka jest różnica między przypisaniem D.id = 666; a wyszukaniem pola o nazwie "id" za pomocą refleksji i wywołaniem field.set(d,666)?

0

Różnica jest taka, że pola prywatne każda klasa ma swoje i widzi tylko swoje. ;) To, że nazwy się pokrywają, to przypadek. Kompilator nie protestuje, bo przecież "to prywatne jest". Każdy może sobie prywatnie robić co chce. W hierarchii ABCD będziesz więc miał cztery zupełnie niezależne pola. Przypadkiem o nazwie tej samej.

Pola protected albo public nie mają kolizji nazw, bo nie masz prawa w dziedziczeniu nadpisać deklaracji takiego pola - kompilator nie pozwala na kolizje.

Rzuć okiem na przykład poniżej, klasy A B C D. Każda ma pole prywatne. Każda ma metodę print (publiczną), która wypisuje SWOJE pole, oraz wywołuje super.print. Na ekranie latają 4 różne wartości. Na modyfikacje pól mogę sobie w przykładzie pozwolić, bo trzymam referencje do Field. Gdybym miał tylko nazwę "id", jako String, to nie wiedziałbym na rzecz której klasy zawołać getField("id").

package net.ranides.annotate;

import java.lang.reflect.Field;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {

        D object = new D();
        
        Class Dcc = object.getClass();
        Class Ccc = Dcc.getSuperclass();
        Class Bcc = Ccc.getSuperclass();
        Class Acc = Bcc.getSuperclass();

        Field Did = Dcc.getDeclaredField("id");
        Field Cid = Ccc.getDeclaredField("id");
        Field Bid = Bcc.getDeclaredField("id");
        Field Aid = Acc.getDeclaredField("id");

        Aid.setAccessible(true);
        Bid.setAccessible(true);
        Cid.setAccessible(true);
        Did.setAccessible(true);
        
        object.print();
        System.out.println();
        
        Aid.setInt(object, 66677);
        Bid.setInt(object, 66678);
        
        object.print();
        System.out.println();
        
        Cid.setInt(object, 66679);
        Did.setInt(object, 66680);
        
        object.print();
        System.out.println();


    }
}
class A {

    private int id = 11;

    public void print() {
        System.out.println("a.id = " + id);
    }
}

class B extends A {

    private int id = 22;

    @Override
    public void print() {
        System.out.println("b.id = " + id);
        super.print();
    }
}

class C extends B {

    private int id = 33;

    @Override
    public void print() {
        System.out.println("c.id = " + id);
        super.print();
    }
}

class D extends C {

    private int id = 44;

    @Override
    public void print() {
        System.out.println("d.id = " + id);
        super.print();
    }
}
0

O polach private nie rozmawiamy, bo ich nie można zmienić przy pomocy refleksji (metoda set z klasy Field). Jeżeli chodzi o pola protected, to mamy różne kompilatory.
Mój bez protestów kompiluje takie coś:

public class Pierwsza
{
    protected int test = 1;
    protected int pierwsza = 1;
}
public class Druga extends Pierwsza
{
    protected int test = 2;
    protected int druga = 2;
}
0

Nie mozna usawic pola private za pomoca reflekcji?? Pierwsze slysze, az przetestuje.

0

Ja to mam chyba zupelnie inny runtime niz Ty (ponizsze wypisze 17):

import java.lang.reflect.Field;


public class TestRef {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Foo f = new Foo();
        Field field = f.getClass().getDeclaredField("f");
        field.setAccessible(true);
        field.set(f, 17);
        f.print();
    }
}


class Foo {
    private int f;

    void print() {
        System.out.println(f);}
}

Co wiecej, mozna za pomoca reflekcji nawet zmieniac pola final.

0

@mięntus, masz rację. Testowałem bez wywołania setAccessible(true).
@Ranides, Twój kod po zmianie modyfikatora private na protected wypisuje te same wyniki.

0

ogólnie bogdans, to mam wrażenie, że obaj jesteśmy nie-dzisiejsi, gubimy sie w szczegółach - zresztą nic dziwnego, bo latanie po wszystkich polach po całej hierarchii za pomocą getDeclaredField to nie jest zbyt częste ;D Na ten moment mamy sytuację:

  • wszystko da się zmieniać, prywatne oczywiście też. przecież wszystkie frameworki do serializacji radośnie private'y zmieniają. wszystkie ORMy sobie z tym radzą tak samo. itd itp ;)

  • wszystkie pola da sie duplikować, nawet public. Za dużo z PMD pracuję podpiętym na sztywno do procesu kompilacji. PMD się o takie rzeczy pluje, kompilator na zasłanianie pozwala ;)

Co jeszcze bardziej nas naraża na ryzyko tego, że będziemy mieć w hierarchii A B C D 4 różne pola, a o nazwie tej samej. Jak się private pozmienia na protected, a nawet public, to problem występuje nadal. Nie ma szans - samą nazwą nie identyfikujemy pola w sposób jednoznaczny. Jeśli używamy getDeclaredField, to musimy także pamiętać, kto zadeklarowane pole deklarował.

0

Panowie

Ja wychodzę z założenia, że programista bawiący się refleksją i ustawiający jej prywatne składowe klasy - wie co robi.

Po pierwsze moja funkcja jest przeznaczona dla klasy która używa adnotacji JEE, i ma ona za zadanie ustawiać tylko pola wybranych typów oraz tych które mają/nie mają odpowiednich adnotacji. Więc warto w przypadku "bawienia" się refleksją mieć na uwadze kontekst rozwiązania.

1

Ależ jasne, po prostu wiesz, że w takiej-a-takiej sytuacji masz taki a taki problem - i tyle ;) Twój wybór, czy nie uwzględnisz tego w kodzie - i napiszesz w dokumentacji, o "undefined behavior". Czy może jednak napiszesz te kilka linii, żeby wykonać check. Czy w ogóle nic nigdzie nie napiszesz. Nie mój cyrk, nie moje małpy :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1