tifyty

pure Java, what else ?

Találós kérdés

A következő kérdésre szerintem senki nem tudja kapásból a választ, és … talán nem is érdemes tudni. Végül is a Java nyelvet használni akarjuk, és nem minden apró marhaságát részletesen ismerni. De azért gondolkodósnak itt a találós kérdés:

Van valahol egy metódus, amelyik public, és minden olyan struktúra, amelyik körülötte van szintén public. A fordító mégsem engedi, hogy meghívjuk, csak reflection-ön keresztül lehet meghívni, és nem is kell beállítani a setAccessible(true)-val az elérhetőséget, mert hát valóban public.

package enumTest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class NonFinalEnumTest {
    public enum A {
        A01 {
            public void println() {
                System.out.println("This is println in " + this);
            }
        },
        A02;
    }
    public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        A a = A.A01;
        Method m = A.A01.getClass().getDeclaredMethod("println", (Class[]) null);
        //m.setAccessible(true);
        m.invoke(A.A01, null);
    }
}

Ezek a kérdések úgy keletkeznek, hogy fejlesztés közben belefutok valamibe, amit először nem értek, aztán utánaolvasok, és akkor rájövök, hogy milyen ocsmány csűrcsavarokat lehet még csinálni, és utána kitalálom, hogy ebből hogyan lehet jó fejtörőt készíteni. Mindenki azt csinál hobbiból amit szeret, meg tud. Van aki keresztrejtvényt fejt, van aki meg tehenet.

Amikor kitalálok egy egy ilyen kérdést, akkor persze többnyire nem egy jön össze, és akkor ezeket elrakom, és amikor nincs időm hétvégén sem, akkor szerdán csak rákattintok, hogy “publish”. 2013. októberben így jött ki a négy Java Horror cikk is, egyéb idő hiányában (amikor ezt írom, épp a harmadikat publikáltam, és lesz/volt még egy). És ez a cikk is megvan már vagy hat hete a forráskódig bezárólag. És most kinyitottam, mert fogalmam nem volt a válaszról, és vagy két percig néztem, mire rájöttem, hogy mit hogy és miért. Pedig én írtam a cikket másfél hónapja. (És még annyi lesz kb. mire kikerül.)

6 responses to “Találós kérdés

  1. Farkas Péter november 6, 2013 1:00 du.

    Egy anonymous class-os megoldáson gondoltam, ami elég hasonló lett.

  2. Verhás István november 6, 2013 1:42 du.

    Az enum tényleg egy külön világ, de azért nem kell egyből a reflection-hez nyúlni. Valójában ugye azt deklaráltad, hogy az ‘a’ egy ‘A’ típus amiben ugye nincs println. Az igaz, hogy amikor értéket kap akkor az ‘A’ egy leszármaztatott osztályával, mert ugye az enum elemek azok, kap értéket amiben már van println de ettől még az ‘a’ referenciád az továbbra is egy ‘A’ típusra mutat. Ha ezt így végig gondoljuk akkor már kezünkben a megoldás pl. a következő egyszerű módon:

    package enumTest;
    
    public class NonFinalEnumTest {
    
        public enum A {
    
            A01 {
                public void println() {
                    System.out.println("This is println in " + this);
                }
            },
            A02;
    
            public void println() {
            }
        ;
    
        }
        public static void main(String[] args) {
            A a = A.A01;
            a.println();
        }
    }
    

    Itt már az ‘A’-nak van println metódusa ami akár lehetne abstract is.

    • Peter Verhas november 6, 2013 5:01 du.

      De a példában az A enum-nak *nincs* println metódusa. Tehát reflection nélkül megoldottuk úgy, hogy közben módosítottunk az eredeti feladaton. Ez nem minden esetben lehetséges.

  3. Verhás István november 7, 2013 2:50 de.

    A találós kérdésre adott példa nem állja ki a próbát ugyanis nem *csak* reflection-el lehet meghívni. Mivel az anonymous class is kap nevet ezért azzal a névvel amit fordításkor kap lehet cast-olni az ‘a’-t az alábbi módon:

        public static void main(String[] args) {
            A a = A.A01;
            ((NonFinalEnumTest$A$1)a).println();
        }
    

    Ez persze így nem fordul le egyből, de két menetben igen. Ha már megvan a NonFinalEnumTest$A$1.class és cleant-t nem mondunk a maven-nek csak install-t akkor vígan lefordítja a javac és működik is az enum módosítása nélkül.
    Jelen ismereteink szerint tehát a helyes válasz a találós kérdésre az, hogy nincs ilyen metódus. 🙂

    • Peter Verhas november 7, 2013 10:38 de.

      Ez csak akkor működik, ha az a Java fordító amit éppen használunk a NonFinalEnumTest$A$1 nevet adja az anonymous osztálynak.

      De az anonymous osztály nem véletlenül anonymous. Nincs hivatalos neve. Olyan, mint amikor valakit nem keresztelnek meg. Persze valahogy akkor is szólítják, de attól az a név még nem garantált. És bármikor el tud romlani a dolog. Nem is kell Java futtatókörnyezetet váltani sem. Elég annyi, hogy létrehozok egy NonFinalEnumTest$A$1 osztályt ugyanabban a csomagban, és a generált név máris valami más lesz. Például itt előttem egy Windows 7 alatt futó Java 1.7.0_09 verzió esetén NonFinalEnumTest$A$2.

      • Verhás István november 7, 2013 12:30 du.

        Valóban törékeny területre érkeztünk. De hogyan is jutottunk ide? Azt mondtad, hogy nem mindig lehet definiálni az enum-ban a metódust, ami gondolom azt jelenti, hogy nem lehet módosítani a forrást hanem csak lefordított jar formájában áll rendelkezésre. Ebben az esetben nem fog megváltozni a neve hiszen nem fordítjuk újra. Persze ettől még definiálhatunk NonFinalEnumTest$A$1 osztályt ugyanabban a csomagban de onnan kezdve már csak a classloaderen múlik, hogy melyiket használja. És persze más osztályokat is tehetünk hasonlóan kétségessé.
        Ha már ott tartunk, hogy mennyire stabil egy megoldás, bár ez nem szerepelt a találós kérdésben de a fixáras projektek már csak ilyenek, akkor az is elgondolkodtató, hogy nem lehet módosítani a forrást. Ha nem áll rendelkezésünkre a forrás és nincs supportunk hozzá vagy olyan supportunk van hozzá ami nem teszi lehetővé ezt a módosítást az sem mondható egy stabil megoldásnak.

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: