Az mennyire számít szokványos dolognak Javaban, hogy valaki "rendes" alkalmazásfejlesztéshez nem használ GUI editort?
Eddig inkább Lazarust használtam (Delphi klón), ami egész jó kódot generál automatikusan is, tehát olvasható, én sem tudnám jobban megírni, viszont mostanában egyre inkább Java felé fordulok főként a nagyobb támogatás miatt, de nem vagyok oda sem az Eclipse, sem a NetBeans által generált kódtól. Nagyjából értem, amit csinál - nem mindig - de nem tetszik, én nem úgy csinálnám. Viszont nem tudom, hogy összetettebb sok ablakos program esetén a GUI editor hiánya mennyire okoz majd gondot. Nekem ez több, mint hobbi, de nem ebből élek. Érdekelne, hogy a profik vagy legalábbis akiknek ez a munkájuk, azok esetén mi a jellemző GUI editor ügyben.
String-gel viszonylag triviálisan működik, de azért vannak trükkös esetek:
1. Mi van, ha a paraméter/visszatérési érték deklarált tipusa egy interface?
2. Mi van, ha a paraméter/visszatérési érték tipusa egy enum?
3. Mi van, ha a feldekorált metódus kifejezetten számít null értékre?
4. Mi van, ha a behelyettesítendő érték generikus tipus?
5. Mi van, ha véletlenül egy metódust kifelejtesz?
De nekem személy szerint elvi problémám van az ilyen megoldásokkal, mégpedig az, hogy ha a program szarul lett megírva, akkor igenis hasaljon el, lehetőleg minél hamarabb. Ezért mondom azt, hogy elfedés helyett inkább már fordítási időben ki kéne szúrni ezeket a problémákat.
De ha mégis a másik utat választod, akkor is szerintem jobban jársz, ha annotációkkal vezérled a működést.
Hát akkor egy kicsit változik a megoldás, de az alapelv ugyanaz: a kombinált container kell gondoskodjon róla, hogy csak úgy lehessen példányosítani, ahogy értelme van.
Ezért nem szeretem pl. azokat a C stílusú megoldásokat, ahol az eredményt paraméterben adja valaki vissza, a visszatérési értékben meg egy hibakódot/objektumot, mert azt a hívó szinte garantáltan elfelejti majd ellenőrizni.
A fordított variáns, amit te írtál, ahol a hibát adjuk vissza paraméterben, sokkal jobb ebből a szempontból, mert nem lehet véletlenül nem kezelni a hibát, csak szándékosan. Akkor meg megérdemlik. :)
Java-ban a Google Guava Objects/MoreObjects.firstNonNull() az, ami ilyesmire szolgál.
Az általad vázolt opciók közül én mindenképp a becsomagolós megoldást választanám, mert bár az a legnyakatekertebb, még mindig az a leginkább olvasható és az fejezi ki legjobban, hogy mi történik. Ha többféle hibakód is lehetséges, de a hibáknak nincs egyéb paramétere, akkor meg valami ilyesmit csinálok:
public enum Eredmeny {
OK, HIBA1, HIBA2;
public static final class EredmenyContainer { private final Object hurra; private final Eredmeny hiba; private EredmenyContainer(Eredmeny hiba, Object hurra) { this.hiba = hiba; this.hurra = hurra; } }
public EredmenyContainer newEredmeny( Object hurra ) { if ((this == OK) == (hurra == null)) throw new IllegalArgumentException( "..." ); return new EredmenyContainer(this, hurra); } }
Ezzel garantálom, hogy akkor és csak akkor adok vissza nullt, ha hiba van, és nem kell a hívó fél jóindulatára hagyatkoznom.
scala-ban az options úgy működik, hogy options.getOr(defaultOjjektum)
vagy options.getOrThrow(new GazVanException)
így nincs null pointer ellenőrzés - vagy csak nem látom mert bele van ágyazva az options függvényekbe
viszont az olvashatóságon javít
olyan konkrét példa volt - nem feltétlenül jó tervezés - hogy a válasz egy Collection
amiben vagy van elem, vagy nincs...
(ez persze átverés, mert egy másik fejlesztő azt várná hogy elemek lesznek benne többes számban, azért is jó hogy van ez az Option)
... így ha az elem feldolgozása berakható egy for (X x: c) ciklusba, amit semmit sem csinál ha üres a c Collection
de ezen spórolás nem lesz, és szebb sem lesz a kód, csak robosztusabb
komplex hibakezeléssel terhelt függvényeknél szaladtam bele néhányszor abba a dilemmába, hogy a hiba objektumot és a normál objektumot hogyan adjam vissza
itt most hiba alatt felhasználói hibát kell érteni, vagy más üzletileg értelmezett elágazást a normál folyamattól
exception handlinget elvetettem mondván nem célszerű control flow-ra használni, ami itt a tényleges funkció, lassú lesz, felesleges stack trace-ek keletkeznek, aminek nincs szerepe mert nem bugot kell keresni, a user elgépelte a dátumot
az egyik opció az volt, hogy olyan visszatérési típust választok, amibe becsomagolható a hiba és a normál válasz is, vagy olyan általános a típusa pl. Objektum, amiben visszajöhet a hiba is, és sima elágazásokkal irányt lehet a hívó oldalon váltani
a másik, hogy paraméterben adok vissza hibát, pl egy collection, amiben a folyamat közben gyűlnek a hibák, és a hívó kód egyből látja ha nem üres
Off: Ha C-ben lennénk, azt javasolnám neki, hogy egyáltalán ne adjon vissza objektumot néven keresztül, hanem a visszaadott érték mindig a hibakód legyen (pl: 0: ok; 1: nincs találat; egyéb: nagy baj van), az 'igazi' válasz paramétereken keresztül jöjjön:
int Employee_GetByName (String name, Employee **retptr);
Ilyenkor a függvény nullt nem adhat vissza, viszont cserébe kapunk egy helyesnek látszó, de mégsem a várt választ tartalmazó default objektumot, amivel lehet műveleteket végezni, de simán félrevihetik az alkalmazást.
És pont ezért jó, hogy van null. Persze kicserélhetnénk egy objektumra, amivel semmit nem lehet csinálni, és mennyivel jobb lenne, ha nem if (x==null)-okkal lenne tele a kód, hanem if (x==Null.of()) lenne helyette.
A megoldás szerintem egyáltalán nem a null eltörlése, hanem a fordító felokosítása lenne. Annak idején fejlesztett valaki PhD disszertáció gyanánt egy Nice nevű nyelvet, ami ugyanúgy a JVM-re épült (akkoriban ez eléggé újdonságnak számított, Scala még a fasorban se volt, Groovy meg pláne nem), és a Nice egyik alappillére az volt, hogy kétféleképp deklarálhattál változót:
private String x; // ez nem lehet null
private String? x; // ez viszont igen
És persze ugyanez metódusok paramétereire és visszatérési értékeire. A fordító pedig szépen ellenőrizte és betartatta, hogy null-t ne adhass át olyan helyen, ahol nem várják.
private void bar( Object x ) {...}
private void foo(Object? x) {
bar( x ); // ilyet nem csinálhattál
if (x != null) { //ilyet viszont igen
bar( x );
}
}
Az ötlet nyilván nem eredeti, bár most hirtelen nem jut eszembe, milyen másik nyelvekben van, viszont zökkenőmentesen beépíthető lenne a Java nyelvbe is, csak meg kéne fordítani: a jelöletlen deklaráció jelenti azt, hogy lehet null, és egy másik jelet (pl. !) használni akkor, ha a null tiltva van.
olvastam egy olyan blog cikket, amiben a szerző hosszan elmélkedett a null káros mivoltáról
a mondandó lényege az volt, hogy nem objektum-szerű az a szemlélet, hogy egy függvény vissza tud adni valamit ami maga nem objektum, és hogy a függvény válaszát kezelő kódnak kezelnie kell tudni a null választ is
a szerző a null object mintát propagálta,
volt a cikk mögött egy jó kis vita is, hogy mi a jó megoldás, előkerült az Options<> meg egyebek
miközben jöttem hazafelé az jutott eszembe, hogy a null reference képezte egyik részproblémára lehetne egy jó kis aspect-es megoldást adni
a null válasz egyik részproblémája, amikor default értéket szeretnél visszakapni, ami tulajdonképpen ugyanaz mint a fent említett null object minta, részben azért mert nem akarod telerakni a kódodat if ( != null) elágazásokkal, amelyek ráadásul ismétlik önmagukat, vagy mert attól tartasz, hogy valahol el fogod felejteni a null check-et és élesben omlik össze miatta az alkalmazás, legyen valami ami ettől véd, a hibát felfedi és később lehet javítani
a másik részprobléma lenne hogy az adott kódban egy teljesen kódspecifikus elágazásra van szükséged, de ezt értelemszerűen nem lehet központosítani
Szóval az lenne a lényeg, hogy létrehozok egy aspect-et amit ráakasztok minden olyan függvényre, aminek a visszatérési értéke a kezelendő típusba tartozik, legyen ez a példa kevéért a String. Tehát minden String-et visszaadó függvényre ráakasztok egy olyan aspect-et, ami egy helyen megvizsgálja hogy amit a függény visszaadna az null-e. Ha igen, kicseréli egy nem-null default-ra, és azt adja vissza.
Ilyenkor a függvény nullt nem adhat vissza, viszont cserébe kapunk egy helyesnek látszó, de mégsem a várt választ tartalmazó default objektumot, amivel lehet műveleteket végezni, de simán félrevihetik az alkalmazást.
Elég gyakori eset lehet, hogy keresek egy értéket, ha nincs, jó helyette egy default is, ami még üzleti tartalom szerint is helyes.
2: nem találja a com osztályt (de néhol a debugger bugos a netbeans-ban és ott is hibát talál, ahol nincs, vagy más soron jelez hibát)
Én ugyan nem vonom kétségbe, hogy előfordulhat egy fordítóprogramban is hiba, természetesen, de azért induljunk ki abból, főleg, hogy ha magadat kezdő programozónak nevezed, és te nyilván tudod, hogy nem. Vedd kiindulási alapnak, hogy száz hibából száztíz a sajátod lesz, és csak a maradék egy lehet, talán, esetleg másé...
Esetleg (csak találgatok, de hátha beválik), ha beírod, hogy milyen IDE-ben kattintgatsz, akkor lesz valaki, aki tudja, hogy hová kell klikkelni benne a classpath beállításhoz.
És hogyan rajzoltatnál ki +D-ben pontokat úgy, hogy jelölve legyen egy tisztességes kooridnáta-rendszerben, melyik pont, hol van. Ez matlabban szánalmasan egyszerű, java-ban pedig napok óta nem boldogulok, pedig sokan örülnének neki.
Miért működne, én nem töltöttem le ezt a csodaprogramot...
Egyébként az az érzésem, hogy te valami kattintgatós eszközzel (más szóval IDE-vel) harcolsz, és egyszerűen nem találod benne a Fordítási beállítások menüpontot... ebben persze távvezérléssel nemigen lehet segíteni.