Metaprogramovanie / Reflexia — pokračovanie

Sergej Chodarev

Metaprogramovanie 3

Reflexia - pokračovanie

Sergej Chodarev

Nielen štruktúra, ale aj vykonávanie

Volanie metódy

Ako to funguje?

Čo potrebujeme pre zavolanie metódy?

Čo potrebujeme počas vykonávania metódy?

Čo potrebujeme pri návrate z metódy?

Zásobník volaní

Zásobník volaní (call stack)

Stack overflow :-(

Tail call optimization

Tail call

function foo(data) {
    ...
    return bar(data);
}

Klasický faktorial

(define (factorial n)
    (if (= n 1)
        1
        (* n (factorial (- n 1)))))
  

Structure and Interpretation of Computer Programs

Faktoriál s tail call

(define (factorial n)
    (define (iter product counter)
        (if (> counter n)
            product
            (iter (* counter product)
                  (+ counter 1))))
    (iter 1 1))

Ako sa dostať k týmto údajom?

Stack trace

    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
          at Main.sayHello(Main.java:8)
          at Main.main(Main.java:4)
  

Introspekcia zásobníka volaní

StackTraceElement

Príklad

Logger

    public interface Logger {
        void logRecord(String className,
                       String methodName,
                       int lineNum,
                       String message,
                       int logRecordType);
        ...
    }
  

Lepší Logger

    public interface Logger {
        void logRecord(String message,
                       int logRecordType);
        ...
    }
  

Implementácia

  public void logRecord(String message, int logRecordType) {
      Throwable ex = new Throwable();
      StackTraceElement ste = ex.getStackTrace()[1];
      String callerClassName = ste.getClassName();
      String callerMethodName = ste.getMethodName();
      int callerLineNum = ste.getLineNumber();
      ...
  

Java9

StackWalker


StackWalker walker = StackWalker.getInstance(
                          Option.RETAIN_CLASS_REFERENCE);
Optional<Class<?>> callerClass = walker.walk(s ->
    s.map(StackFrame::getDeclaringClass)
        .filter(interestingClasses::contains)
        .findFirst());
  

Ďalšie informácie

Čo sa vlastne vykonáva?

Inštrukcie – bytecode

Java bytecode

int x = 5;
int y = 10;
int z = x + y * 7;
 0: iconst_5
 1: istore_1
 2: bipush    10
 4: istore_2
 5: iload_1
 6: iload_2
 7: bipush    7
 9: imul
10: iadd
11: istore_3

Súbor .class

Načítanie triedy

Príklad — dynamické načítanie triedy

String dbClassName = props.getProperty("dbClass",
                                       "sk.tuke.StubDB");
Class dbClass = Class.forName(dbClassName);
customerDB = (CustomerDatabase) dbClass.newInstance();

Dynamické načítanie triedy

ClassLoader

Ako získať ClassLoader

Každá trieda je identifikovaná

  1. Plným názvom
  2. ClassLoaderom

Vieme vytvoriť novú triedu?

Generovanie kódu

  1. Generovanie zdrojového kódu triedy
  2. Preklad
  3. Načítanie pomocou ClassLoader

Generovanie a modifikovanie bajtkódu

Iná možnosť?

Poznáte návrhové vzory?

Proxy pattern

Provide a surrogate or placeholder for another object to control access to it.

Design Patterns: Elements of Reusable Object-Oriented Software

Proxy pattern

Dynamické proxy

Použitie

    Class c = getProxyClass(SomeInterface.class.getClassLoader(),
                        SomeInterface.class);
    Constructor cons = c.getConstructor(InvocationHandler.class);
    Object proxy = cons.newInstance(new SomeIH(obj));
  

Alebo jednoduchšie


Object proxy = Proxy.newProxyInstance(
                      SomeInterface.class.getClassLoader(),
                      new Class[]{ SomeInterface.class },
                      new SomeIH(obj));
  

InvocationHandler

Implementácia InvocationHandler

    public Object invoke(Object proxy, Method method, Object[] args)
                          throws Throwable {
        return method.invoke(target, args);
    }
  

Príklad

Trasovanie (tracing)

Riešenie

Zdrojový kód

Použitie