Vállalati Információs Rendszerek

Java annotációk és reflection API

Annotációk

Feladatok:

  • Írjunk egy egyszerű, paraméter nélküli annotációt, amely tetszőleges kódelemre használható
In [46]:
public @interface MyAnnot { }

@MyAnnot
public class A {
    
    @MyAnnot
    private int a;
    
    @MyAnnot()
    public int getA() {
        return a;
    }
}
  • Készítsünk egy String paraméteres annotációt, amely csak metódusokra használható, és futásidőben is elérhető
In [48]:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyStringAnnotation {

    public String param();
}
In [54]:
@MyStringAnnotation(param="test")
public class B {
    
}
|   @MyStringAnnotation(param="test")
annotation type not applicable to this kind of declaration
In [55]:
public class B {
    @MyStringAnnotation(param="test")
    public void f() {
    
    }
}

Reflection API

Feladatok:

  • RentCar segéd entitás definiálása
In [56]:
public class RentCar {

    private int rate;
    private String type;
    public int price;

    public RentCar(int length) {
        if (length < 455) {
            type = "small";
            rate = 35;
        } else if ((length >= 455) && (length < 495)) {
            type = "mid-sized";
            rate = 45;
        } else if (length >= 495) {
            type = "large";
            rate = 55;
        }
    }

    public int getRate() {
        return rate;
    }

    public String getType() {
        return type;
    }

    public void setRate(int rate) {
        this.rate = rate;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void computeRentalCost(int numDays) {
        price = numDays * rate;
        System.out.println("The cost of your rental car is " + price + " euros");
    }
}
  • Class objetum, osztály jellemzők kinyerése
In [59]:
Class<RentCar> rental = RentCar.class;

String rentalClassPackage = rental.getName();
System.out.println("Class Name is: " + rentalClassPackage);

String rentalClassNoPackage = rental.getSimpleName();
System.out.println("Class Name without package is: " + rentalClassNoPackage);

Package rentalPackage = rental.getPackage();
System.out.println("Package Name is: " + rentalPackage);
Class Name is: REPL.$JShell$86$RentCar
Class Name without package is: RentCar
Package Name is: package REPL
  • Konstruktorok listázása, új objektum példányosítása
In [61]:
import java.lang.reflect.Constructor;

Constructor<?>[] constructors = rental.getConstructors();
System.out.println("Constructors are: " + Arrays.toString(constructors));

Constructor<RentCar> constructor = rental.getConstructor(Integer.TYPE);

RentCar rent = constructor.newInstance(455);
Constructors are: [public REPL.$JShell$86$RentCar(int)]
  • Metódusok listázása, feltérképezése és meghívása reflection segítségével
In [62]:
import java.lang.reflect.Method;

Method[] allmethods = rental.getMethods();
System.out.println("Methods are: " + Arrays.toString(allmethods));
for (Method method : allmethods) {
    System.out.println("method = " + method.getName());
}

Method[] declaredMethods = rental.getDeclaredMethods();
System.out.println("Declared Methods are: "
        + Arrays.toString(declaredMethods));
for (Method dmethod : declaredMethods) {
    System.out.println("method = " + dmethod.getName());
}

Method oneMethod = rental.getMethod("computeRentalCost", Integer.TYPE );
System.out.println("Method is: " + oneMethod);

oneMethod.invoke(rent, 4);

Class<?>[] parameterTypes = oneMethod.getParameterTypes();
System.out.println("Parameter types of computeRentalCost() are: "
        + Arrays.toString(parameterTypes));

Class<?> returnType = oneMethod.getReturnType();
System.out.println("Return type is: " + returnType);
Methods are: [public void REPL.$JShell$86$RentCar.computeRentalCost(int), public void REPL.$JShell$86$RentCar.setType(java.lang.String), public int REPL.$JShell$86$RentCar.getRate(), public void REPL.$JShell$86$RentCar.setRate(int), public java.lang.String REPL.$JShell$86$RentCar.getType(), public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
method = computeRentalCost
method = setType
method = getRate
method = setRate
method = getType
method = wait
method = wait
method = wait
method = equals
method = toString
method = hashCode
method = getClass
method = notify
method = notifyAll
Declared Methods are: [public void REPL.$JShell$86$RentCar.computeRentalCost(int), public void REPL.$JShell$86$RentCar.setType(java.lang.String), public int REPL.$JShell$86$RentCar.getRate(), public void REPL.$JShell$86$RentCar.setRate(int), public java.lang.String REPL.$JShell$86$RentCar.getType()]
method = computeRentalCost
method = setType
method = getRate
method = setRate
method = getType
Method is: public void REPL.$JShell$86$RentCar.computeRentalCost(int)
The cost of your rental car is 180 euros
Parameter types of computeRentalCost() are: [int]
Return type is: void
  • Mezők kezelése, lekérés, módosítás (akár private esetben is!!!)
In [63]:
import java.lang.reflect.Field;

Field[] fields = rental.getFields();

System.out.println("Public Fields are: ");
for (Field oneField : fields) {
    Field field = rental.getField(oneField.getName());
    String fieldname = field.getName();
    System.out.println("Fieldname is: " + fieldname);

    Object fieldType = field.getType();
    System.out.println("Type of field " + fieldname + " is: " + fieldType);

    Object value = field.get(rent);
    System.out.println("Value of field " + fieldname + " is: " + value);
}

Field privateField = RentCar.class.getDeclaredField("type");

String name = privateField.getName();
System.out.println("One private Fieldname is: " + name);
privateField.setAccessible(true);

String fieldValue = (String) privateField.get(rent);
System.out.println("fieldValue = " + fieldValue);
Public Fields are: 
Fieldname is: price
Type of field price is: int
Value of field price is: 180
One private Fieldname is: type
fieldValue = mid-sized

Összetett feladat:

  • Egy saját annotáció egyetlen decoratorFunction metódussal, aminek nincs default értéke, és metódusokra alkalmazható
    • Ez az annotáció azt fogja jelenteni, hogy ha egy metódusra rárakjuk, akkor annak a metódusnak a hívása helyett a decoratorFunction-al megadott nevű metódus fog hívódni, ami paraméterben megkapja az adott függvényt (Supplier, azaz paraméter nélküli, adott típusú értéket visszaadó függvény)
    • Egy DecoratedClass nevű osztályt, amelynek van egy String-et visszaadó f() és g() nevű metódusa, valamint egy myDecorator(Supplier f) metódusa
      •  Az f és g metódusok tetszőleges String-et adjanak vissza
      •  A myDecorator pedig egy dekorátor metódus legyen, azaz a paraméterben kapott függvény hívásaként előálló String-et dekorálja ki valamivel (pl. elé és mögé szúrjon be valamit, és ezzel a módosított String-gel térjen vissza)
      • Az f metódus legyen beannotálva a „Decorate” annotációval, és decoratorFunction-ként állítsuk be neki a myDecorator metódus
  • Írjunk egy Main osztályt, amely példányosítja a DecoratedClass osztályt és meghívja a myDecorator metódusát az f függvénnyel paraméterként
(2 pont)
  • Készítsünk egy ReflectCaller osztályt, amely
    •  ReflectCaller(Class<?> clazz) konstruktorral rendelkezik, a paraméterben kapott osztály példányán kell reflection segítségével műveleteket végezni
    •  Legyen egy public T callFunc(String methodName) metódusa, amely a paraméterben kapott metódust reflection segítségével meghívja
      •  Ha nincs rajta „Decorate” annotáció, akkor egyszerűen meghívja a metódust egy új objektum példányon (a metódus paraméter nélküli, és a visszatérési értéket cast-olhatjuk T-re)
      • Ha van rajta „Decorate” annotáció, akkor nem a paraméterben megadott nevű metódust hívjuk meg, hanem az annotációban megadott dekorátor metódust az eredeti metódust paraméterként átadva
(3 pont)