Refleksja w Javie

Dostęp do informacji o typie, polach i metodach, dynamiczne ładowanie klas


dr inż. Aleksander Smywiński-Pohl

apohllo@agh.edu.pl

http://apohllo.pl/dydaktyka/programowanie-obiektowe

konsultacje: wtorek 15:30 - 18:00, pokój 4.61

In [ ]:
class A {
    private int a = 0;
    class B {
        private int a = 10;
        
        public void print(){
            System.out.println(a);
            System.out.println(this.a);
        }
    }
    
    public void print(){
        new B().print();
    }
}

new A().print();

Ignoratia juris nocet

Nieznajomość prawa szkodzi (sentencja łacińska)

Kodeks wykroczeń

Art. 124. § 1. Kto cudzą rzecz umyślnie niszczy, uszkadza lub czyni niezdatną do użytku, jeżeli szkoda nie przekracza 250 złotych,podlega karze aresztu, ograniczenia wolności albo grzywny.

§ 2. Usiłowanie, podżeganie i pomocnictwo są karalne.

§ 3. Ściganie następuje na żądanie pokrzywdzonego.

§ 4. W razie popełnienia wykroczenia można orzec obowiązek zapłaty równowartości wyrządzonej szkody lub obowiązek przywrócenia do stanu poprzedniego.

Kodeks karny

Art. 267. § 1. Kto bez uprawnienia uzyskuje dostęp do informacji dla niego nieprzeznaczonej, otwierając zamknięte pismo, podłączając się do sieci telekomunikacyjnej lub przełamując albo omijając elektroniczne, magnetyczne, informatyczne lub inne szczególne jej zabezpieczenie, podlega grzywnie, karze ograniczenia wolności albo pozbawienia wolności do lat 2.

§ 2. Tej samej karze podlega, kto bez uprawnienia uzyskuje dostęp do całości lub części systemu informatycznego.

§ 3. Tej samej karze podlega, kto w celu uzyskania informacji, do której nie jest uprawniony, zakłada lub posługuje się urządzeniem podsłuchowym, wizualnym albo innym urządzeniem lub oprogramowaniem.

§ 4. Tej samej karze podlega, kto informację uzyskaną w sposób określony w § 1-3 ujawnia innej osobie.

§ 5. Ściganie przestępstwa określonego w § 1-4 następuje na wniosek pokrzywdzonego.

Art. 268. § 1. Kto, nie będąc do tego uprawnionym, niszczy, uszkadza, usuwa lub zmienia zapis istotnej informacji albo w inny sposób udaremnia lub znacznie utrudnia osobie uprawnionej zapoznanie się z nią, podlega grzywnie, karze ograniczenia wolności albo pozbawienia wolności do lat 2.

§ 2. Jeżeli czyn określony w § 1 dotyczy zapisu na informatycznym nośniku danych, sprawca podlega karze pozbawienia wolności do lat 3.

§ 3. Kto, dopuszczając się czynu określonego w § 1 lub 2, wyrządza znaczną szkodę majątkową, podlega karze pozbawienia wolności od 3 miesięcy do lat 5.

Art. 268a. § 1. Kto, nie będąc do tego uprawnionym, niszczy, uszkadza, usuwa, zmienia lub utrudnia dostęp do danych informatycznych albo w istotnym stopniu zakłóca lub uniemożliwia automatyczne przetwarzanie, gromadzenie lub przekazywanie takich danych, podlega karze pozbawienia wolności do lat 3.

§ 2. Kto, dopuszczając się czynu określonego w § 1, wyrządza znaczną szkodę majątkową, podlega karze pozbawienia wolności od 3 miesięcy do lat 5.

Art. 269b. § 1. Kto wytwarza, pozyskuje, zbywa lub udostępnia innym osobom urządzenia lub programy komputerowe przystosowane do popełnienia przestępstwa określonego w art. 165 § 1 pkt 4, art. 267 § 3, art. 268a § 1 albo § 2 w związku z § 1, art. 269 § 2 albo art. 269a, a także hasła komputerowe, kody dostępu lub inne dane umożliwiające dostęp do informacji przechowywanych w systemie komputerowym lub sieci teleinformatycznej, podlega karze pozbawienia wolności do lat 3.

§ 2. W razie skazania za przestępstwo określone w § 1, sąd orzeka przepadek określonych w nim przedmiotów, a może orzec ich przepadek, jeżeli nie stanowiły własności sprawcy

In [ ]:
class DummyIterator implements Iterator {
    private int index = 0;
    
    @Override
    public boolean hasNext(){
        return false;
    }
    
    @Override
    public Object next(){
        return null;
    }
}
In [ ]:
javac DummyIterator.java
In [ ]:
cat DummyIterator.class
����4
  indexI<init>()VCodeLineNumberTablehasNext()Znext()Ljava/lang/Object;
SourceFileDummyIterator.java

DummyIteratorjava/lang/Objectjava/util/Iterator      
&
*�*��



�
       
�
%
In [ ]:
javap DummyIterator
In [ ]:
Compiled from "DummyIterator.java"
class DummyIterator implements java.util.Iterator {
  DummyIterator();
  public boolean hasNext();
  public java.lang.Object next();
}

Informacje o typie

In [ ]:
import static java.lang.System.out;

out.println(int.class);
out.println(Integer.class);
out.println("Ala ma kota".getClass());
out.println(new String[0].getClass());
out.println(new DummyIterator().getClass());

Weryfikacja przynależności do typu

In [ ]:
Object text = "To kot, a to Ala";
if(text.getClass().equals(String.class)){
   out.println("Obiekt jest stringiem");
}
In [ ]:
Object iterator = new DummyIterator();
if(iterator.getClass().equals(Iterator.class)) {
    out.println("Obiekt jest iteratorem");
}
In [ ]:
if(iterator instanceof Iterator) {
    out.println("Obiekt implementuje interfejs iterator");
}

Klasa nadrzędna

In [ ]:
out.println(new DummyIterator().getClass());
out.println(new DummyIterator().getClass().getClass().getClass().getSuperclass());

Wspólna klasa nadrzędna

In [ ]:
class Animal {}
class Mammal extends Animal {}
class Cat extends Mammal {}
class Whale extends Mammal {}
class Reptile extends Animal {}
class Snake extends Reptile {}
In [ ]:
List<Class<?>> ancestors(Class<?> klass){
    var result = new LinkedList<Class<?>>();
    while(klass != Object.class){
        result.add(klass);
        klass = klass.getSuperclass();
    }
    result.add(Object.class);
    Collections.reverse(result);
    return result;
}
In [ ]:
ancestors(Snake.class);
In [ ]:
Class<?> commonAncestor(Class<?> aClass, Class<?> bClass){
    var aAncestors = ancestors(aClass);
    var bAncestors = ancestors(bClass);
    for(int i = 0; i < aAncestors.size() && i < bAncestors.size(); i++){
        if(aAncestors.get(i) != bAncestors.get(i)){
            return aAncestors.get(i-1);
        }
    }
    return aAncestors.get(aAncestors.size()-1);
}
In [ ]:
out.println(commonAncestor(Cat.class, Snake.class));
out.println(commonAncestor(Cat.class, Whale.class));

Dynamiczne ładowanie klas

In [ ]:
interface IVehicle {
    void go();
}
In [ ]:
class Car implements IVehicle {
    public void go(){
        out.println("Brum, brum");
    }
}
Car.class
In [ ]:
class Airplane implements IVehicle {
    public void go(){
        out.println("Bzzzzzz");
    }
}
Airplane.class
In [ ]:
class Rocket implements IVehicle {
    public void go(){
        out.println("BUUUUUUUUUUUM");
    }
}
Rocket.class
In [ ]:
class VehicleSystem {
    public static void main(String[] args){
        var vehicles = new LinkedList<IVehicle>();
        for(String vehicleName : args){
            try {
                var klass = (Class<IVehicle>) Class.forName(vehicleName);
                vehicles.add(klass.newInstance());
            } catch(ClassNotFoundException ex) {
                System.out.println("The class " + vehicleName +  
                                   " has not been found. ");
                return;
            } catch(InstantiationException | IllegalAccessException ex) {
                System.out.println("The class " + vehicleName +  
                                   " cannot be instantiated. ");
                return;
            }
            
        }
        for(IVehicle vehicle : vehicles) {
            vehicle.go();
        }
    }
}
In [ ]:
String[] classes = {"REPL.$JShell$55G$Car", "REPL.$JShell$57J$Rocket", 
                    "REPL.$JShell$56J$Airplane", "REPL.$JShell$55G$Car"};
VehicleSystem.main(classes);

Dostęp do składowych

  • newInstance()
  • getMethod(...)
  • getMethods()
  • getConstructor(...)
  • getConstructors()
  • getField(...)
  • getFields()
In [ ]:
void prettyPrint(Object[] elements){
    for(Object element : elements)
        out.println(element);
}
In [ ]:
Class<?> klass = DummyIterator.class;

prettyPrint(klass.getMethods());
In [ ]:
prettyPrint(klass.getConstructors());
In [ ]:
prettyPrint(klass.getFields());

Dynamiczne tworzenie instancji

In [ ]:
import java.lang.reflect.*;

Class<?> klass = DummyIterator.class;
Constructor constructor = klass.getConstructor(new Class<?>[0]);
Iterator dummyIterator = (Iterator) constructor.newInstance();

out.println(dummyIterator);
In [ ]:
class DummyIterator implements Iterator {
    private int index = 0;
    
    public DummyIterator(){
    }
    
    public DummyIterator(int index){
        this.index = index;
    }

    @Override
    public boolean hasNext(){
        return false;
    }

    @Override
    public Object next(){
        return null;
    }
    
    public String toString(){
        return "DummyIterator[" + this.index + "]";
    }
}
In [ ]:
Class<?> klass = DummyIterator.class;

Constructor constructor = klass.getConstructor(new Class<?>[]{int.class});
Iterator dummyIterator = (Iterator) constructor.newInstance(1);

out.println(dummyIterator);

Dynamiczne wywołanie metody

In [ ]:
Method hasNextMethod = klass.getMethod("hasNext", new Class<?>[0]);
out.println(hasNextMethod.getName());
out.println(hasNextMethod.getParameterTypes());
In [ ]:
Constructor constructor = klass.getConstructor(new Class<?>[]{int.class});
Iterator dummyIterator = (Iterator) constructor.newInstance(1);

Method hasNextMethod = klass.getMethod("hasNext", new Class<?>[0]);
hasNextMethod.invoke(dummyIterator);

Dynamiczny dostęp do pól

In [ ]:
Field indexField = klass.getField("index");
indexField.getName();
indexField.getModifiers();
In [ ]:
Field indexField = klass.getDeclaredField("index");
out.println(indexField.getName());
out.println(indexField.getModifiers());
In [ ]:
Constructor constructor = klass.getConstructor(new Class<?>[]{int.class});
Iterator dummyIterator = (Iterator) constructor.newInstance(1);

Field indexField = klass.getDeclaredField("index");
indexField.get(dummyIterator);
In [ ]:
indexField.setAccessible(true);
out.println(indexField.get(dummyIterator));
indexField.set(dummyIterator,2);
out.println(indexField.get(dummyIterator));

Dostęp do wszystkich składowych prywatnych

  • getDeclaredMethod(...)
  • getDeclaredMethods()
  • getDeclaredConstructor(...)
  • getDeclaredConstructors()
  • getDeclaredField(...)
  • getDeclaredFields()

Uwaga: składowe dziedziczone nie są zwracane

Dynamiczne proxy

In [1]:
import java.lang.reflect.*;

class MethodReplayer implements InvocationHandler {
    private List<String> list = new LinkedList<>();
    private List<Method> invokedMethods = new LinkedList<>();
    private List<Object[]> invokedArguments = new LinkedList<>();

    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable{
        if(!method.getName().equals("toString")){
            invokedMethods.add(method);
            invokedArguments.add(args);
        }
        return method.invoke(list, args);
    }

    public void replay() throws Throwable {
        for(int i = 0; i < invokedMethods.size(); i++){
            invokedMethods.get(i).invoke(list, invokedArguments.get(i));
        }
    }
}
In [9]:
import static java.lang.reflect.Proxy.newProxyInstance;

MethodReplayer replayer = new MethodReplayer();
List<String> list = (List<String>) newProxyInstance(List.class.getClassLoader(),
        new Class[] { List.class },
        replayer);

list.add("dog");
list.add("cat");

System.out.println(list);


replayer.replay();
System.out.println(list);
[dog, cat]
[dog, cat, dog, cat]

Uchwyty do metod

  • w API refleksji sygnatura argumentów metody jest wskazywana przez Class[]
  • w nowym API mamy nowy typ MethodType:
    • brak nazwy metody
    • brak typu adresata
    • tylko typy argumentów oraz typ zwracanej wartości
In [12]:
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;

// np. String toString()
MethodType toStringType = MethodType.methodType(String.class);

// np. Integer parseInt(String str)
MethodType fromStringToInt = MethodType.methodType(Integer.class, String.class);
In [14]:
class CallMeMaybe {
    public String toString(){
        return "call me maybe";
    }
    
    public Lookup getLookup(){
        return MethodHandles.lookup();
    }
}
In [15]:
CallMeMaybe maybe = new CallMeMaybe();
Lookup maybeLookup = maybe.getLookup();

MethodHandle maybeHandle = maybeLookup.findVirtual(CallMeMaybe.class, "toString", toStringType);
maybeHandle.invoke(maybe);
Out[15]:
call me maybe

Pytania?