PDA

Pogčedajte punu verziju : Java i refleksija


Nemanja Avramović
13. 10. 2011., 14:12
Pravim neki (kao) DB ORM za SQLite (prvenstveno da bih naučio nešto) pa imam sledeću situaciju:

1) (Apstraktna) Klasa SQLiteORM
2) Korisnik iz nje nasledi svoju klasu: Class MojObj extends SQLiteORM i definiše ime tabele i custom funkcije, relacije etc...
3) Kad korisnik u kodu ima:

MojObj obj = new MojObj();
obj.where('kolona', 'vrednost').get();

Ovo treba da mu vrati niz objekata klase MojObj ako ima više rezultata, ili jedan objekat klase MojObj, ako ima jedan rezultat, naravno. Ovo za niz ili jedan obj može da se radi i sa dve f-je, nebitno je.

Međutim, kod za f-ju get() je u klasi SQLiteORM tako da verujem da meni ustvari treba refleksija jer pravim nove instance objekta izvedene klase.

Može neki primer refleksije, tj. kako iz koda natklase (mada je to ustvari objekat potklase, ali ja pišem taj kod u mojoj natklasi) napraviti objekat potklase i pročitati/upisati neka (public, možda neka budu i static, polja), odnosno pozvati neku funkciju? Da li je ovo uopšte moguće uraditi? U PHP-u je moguće jer on nije toliko striktan sa tipovima kao Java npr.

Nemanja Avramović
13. 10. 2011., 18:30
Našao sam dobar dokument na ovu temu ovde: http://java.sun.com/developer/technicalArticles/ALT/Reflection/

Pogledaću pa ću se valjda snaći :)

jablan
14. 10. 2011., 13:59
Pripazi samo kad praviš tu factory metodu na činjenicu da parent klasa ne bi smela da bude svesna postojanja konkretne potklase. BTW, ako već praviš ORM da bi nešto naučio, Java je prilično loš izbor.

Nego, nisam siguran da ti je za to o čemu pričaš neophodna refleksija:


abstract class ModelBase {
public abstract String getTableName();
public String get() {
return "SELECT * FROM " + getTableName() + ";";
}
}

class Radnik extends ModelBase {
public String getTableName() {
return "radnici";
}
}

class Test
{
public static void main(String args[])
{
ModelBase radnik = new Radnik();
System.out.println(radnik.get());
/* SELECT * FROM radnici; */
}
}


Refleksija će ti trebati kad npr. ne budeš hteo da eksplicitno navodiš ime tabele u modelu, već želiš da od imena klase izvedeš ime tabele.

jablan
14. 10. 2011., 14:39
Ustvari lupam, da bi kreirao instance moraćeš da koristiš refleksiju.

degojs
14. 10. 2011., 19:29
BTW, ako već praviš ORM da bi nešto naučio, Java je prilično loš izbor.

Well whaddya know..

Osim što su među najpoznatijim ORM alatima oni koji rade na Javi, kao npr. Hibernate. Sa Javom kvalitetnih ORM biblioteka sigurno ne nedostaje, većina open source, a u praksi se koriste za zaista mission-critical aplikacije već dugo vremena.

Čini mi se i da je ActiveRecord upravo prvo dobio ime i opisan od strane M. Fowlera u njegovoj knjizi Patterns of Ent. Applications, sa primerima u.. Javi i C#-u.

degojs
14. 10. 2011., 19:50
@Nemanja: imaš negde žešći problem sa tim što želiš da uradiš, u samom dizajnu (ideji, zamisli) čitave stvari, jer parent klasa ne bi tebala da poziva funkcije u child klasi..

Negde si pobrkao malo stvari.

Međutim, kod za f-ju get() je u klasi SQLiteORM tako da verujem da meni ustvari treba refleksija jer pravim nove instance objekta izvedene klase.

Novu instancu objekta izvede klase je već napravio onaj koji koristi tu izvedenu klasu, ovde:

MojObj obj = new MojObj();


Ali, ono što tebi verovtno treba jeste da iz izvedene klase vratiš čitav niz objekata tog tipa. Obično bi to značilo da negde u izvedenoj klasi imaš static funkciju koja bi to radila, nešto tipa:


public static List<MojObj> get(parametri bla bla)
{
List<MojObj> ret = new ArrayList<MojObj>();
...
sada ovde nekako, obično pozivajući funkcije parent klase, dovučeš ono što ti treba i objekte lepo dodaš u tu kolekciju).
I onda to vratiš:
...
return ret;

}


Pa bi pozivanje išlo sa:

List<MojObj> lista = MojObj.get(bla bla);


i sada radiš nešto u for-each petlji, npr (proveri sintaksu za for-each petlju u javi, mislim da je ovako):


for( MojObj obj : lista )
{
obj.Balance = 0;
obj.Save(); // verovatno nije potreban Save() na svakom objektu, već na čitavom kontekstu nakon završetka petlje, tj. svih izmena...
}

jablan
14. 10. 2011., 23:04
^ Fora je upravo da instanciranje ne bude u izvedenoj klasi jer bi ta metoda onda glasila potpuno isto u svim izvedenim klasama, osim tog poziva "new MojObj". Zato mu i treba refleksija, da bi dodavanje novog modela moglo da se svodi na "praznu" klasu koja nasleđuje BaseModel. A refleksija je u Javi i C# znatno zapetljanija nego u dinamičkim jezicima.

A glede flejma, nisam ni rekao da u Java ekosistemu nema kvalitetnog softvera (štaviše), već da ima efikasnijih jezika za učenje kako se prave ORM-ovi.

Nemanja Avramović
15. 10. 2011., 00:03
Ehej, polako sa flejmom, naši smo :)

Kao prvo, krenuo sam da radim neke Android aplikacije u Javi pa me smara rad sa bazama, odnosno hteo bih više da se fokusiram na funkcionalnost aplikacije a ne na pisanje SQL-a i pisanje 20 redova koda svaki put kad treba nešto da vučem iz baze. A kako mi je diplomski rad na temu programiranja Android aplikacija verujem da ću raditi još nešto osim tog diplomskog pa reko' da spojim lepo i korisno :D

Drugo, u PHP-u radim sa CodeIgniterom i nekim ORM-om za njega po imenu Datamapper, i iskreno dosta mi se svidelo kako to radi, pa samo pokušavam da napravim nešto slično za Javu (Android) i SQLite, samo ne toliko korisno (čitaj: kompleksno), bar ne za sad.

Dalje, osnovna klasa ne zna za, i ne poziva metode izvedene klase, jer se u izvedenoj klasi nalaze samo metode koje su potrebne za "rad" konkretnog modela (npr. klasa "Radnik", kad smo već krenuli sa tim, treba da ima metodu "izracunajPlatu" ili tako nešto, lupam sad) i osnovna klasa nema potrebe uopšte da ih poziva. Osnovna klasa nikad ne treba (i ne sme) da poziva metode izvedene klase, to mi je jasno i potpuno logično jer osnovna klasa i ne zna da postoje izvedene klase i koje metode u njima postoje.

Fora je što kad izvedena klasa pozove get() koja je u osnovnoj klasi, ta metoda (get) treba preko this.getClass().getName() da vidi ime izvedene klase, i preko refleksije da napravi i vrati objekat te klase, da bi korisnik u vraćenom objektu, pored podataka iz baze, imao i pristup metodama izvedene klase.

miks
15. 10. 2011., 00:29
A da probas nesto da uradis sa genericima. npr. get<T>()

degojs
15. 10. 2011., 00:36
^Upravo to gledam. Moze, mozda.. samo malo :)

Za C# bi moglo, pri cemu samo dodas generics constraint

class bazna {
List<T> get<T>(bla bla) where T : bazna, new() {
}
}

I onda unutar get<T> imas dostupno sve sto definise bazna klasa a vracas objekte tipa T (izvedena).

E sad nisam dugo radio sa Javom, nisam siguran kako bi tamo odradio ove generics constrainte, da li je moguce uopste.

U C# bi onda jednostavno imao:

var izvedena = new Izvedena();
var lista = izvedena.get<Izvedena>(bal bal)
i dobijes nazad iz bazne klase list<Izvedena>.

degojs
15. 10. 2011., 00:49
Izgleda da moze i sa Javom, nisam koristio iste tamo, koliko vidim mozes da imas <T extends Bazna>.

Eto Nemanja, zamrsili smo :D

degojs
15. 10. 2011., 02:34
Ja ne znam moze li jednostavnije, ovako jeste malo zamrseno jer ne znam bolji nacin da uradis jednostavno T x = new T() kao u C#-u.


public class Bazna {

private String _name;
public void setName(String name) { _name = name; }
public String getName() { return _name; }

public<T extends Bazna> List<T> getList(Class<T> type) throws InstantiationException, IllegalAccessException
{
List<T> lista = new ArrayList<T>();
//T prvi = new T(); // mfff!!! oh lord, there ain't no heaven on the county road :(
T prvi = type.newInstance(); prvi.setName( "Prva" );
T drugi = type.newInstance(); drugi.setName( "Druga" );
lista.add(prvi);
lista.add(drugi);
return lista;
}

}


public class Izvedena extends Bazna {}

I onda:
Izvedena izv = new Izvedena();
try {
List<Izvedena> rezultat = izv.getList(Izvedena.class);
for( Izvedena i : rezultat )
{
System.out.println( "Name=" + i.getName() );
}
} catch (Exception e){
}


Sto daje:
Name=Prva
Name=Druga

jablan
15. 10. 2011., 08:45
Tako nešto. S tim što bi ultimativni poziv trebalo da, umesto:

List<Izvedena> rezultat = izv.getList(Izvedena.class);

glasi

List<Izvedena> rezultat = Izvedena.getList();

Tj, 1) nema potrebe da polazim od instance klase, već treba da imam statičku factory metodu i 2) nema potrebe da naglašavam klasu koju želim da mi se vrati, jer statičku metodu već zovem na toj klasi.

degojs
15. 10. 2011., 09:44
^1) Stoji, to smo vec pre razresili, u primeru sam samo gledao da nateram da profercera pa sam zaboravio na to. Tnx.

2) E to mozda nije moguce. U C# to moze bez problema (vidi primer gore u C#), ali u Javi izgleda ne moze drugacije, upravo moras da posaljes kao parametar ono Class<T> tip jer izgleda ne ide ono T prvi = new T() kako sam komentirao u kodu. Mada nisam 100% siguran.

srdjan
15. 10. 2011., 16:06
Android aplikacije u Javi... smara rad sa bazama... pisanje SQL-a i pisanje 20 redova koda svaki put kad treba nešto da vučem iz baze.

Možda ti ne rešava stvar, ali kad već spominjes ovo... ja sam rešio većinu problema te vrste sa jednim modulom i tabelom od par polja: key, type, value_as_string.

(a la Registry)