Reflexe (programování)

Reflexe je v objektově orientovaném programování schopnost programovacího jazyka zjistit za běhu informace o určitém programovém objektu. Obecně v programování je reflexe schopnost zjistit informace o programu a jeho syntaktické struktuře.

V objektově orientovaném programování je program rozdělen do tříd, kdy jednotlivá třída popisuje vnitřní strukturu objektu a jeho vnější rozhraní. Na základně tříd je možné tvořit jednotlivé objekty. Některé jazyky mají schopnost za běhu zjistit informace o daném programu. Tato schopnost se nazývá reflexe, s jejíž pomocí lze získat za běhu programu informace o typu objektu. V objektově orientovaném programování se dá říci, že vše je objekt, tak je tedy objektem i třída a jiné datové typy, o kterých lze zjistit požadované informace.

Využití

Schopnost reflexe je vhodná pro vzdálené zpracování, kdy jsou dva počítače a na jeden z nich přicházejí objekty z toho druhého a počítač, který objekty zpracovává, má možnost získat pomocí reflexe potřebné informace o daném typu objektu. Lze to využít v jakémkoli programu, který za běhu upravuje své chování vůči objektům, se kterými pracuje.

Reflexe v Javě

Informace, které potřebujeme, můžeme získat z instance třídy java.lang.Class. Tento class-objekt mají jak třídy, tak rozhraní i primitivní typy. Při zavádění daného typu do paměti je pomocí instance třídy ClassLoader vytvořen class-objekt, který lze využít při získávání informací o daném typu objektu.[1] Pokud je nutné zajistit dynamické chování programu, v tomto případě je vhodnější[zdroj?] využít jazyky jako Groovy, Jython, JRuby, jejichž překladače řeší dynamičnost pomocí reflexe. V případě, že se liší jednotlivé verze JVM svými knihovnami, pak je možné přizpůsobit chování programu verzi virtuálního stroje, na kterém aktuálně program běží.[2]

Za běhu programu je možné získat:

  • Název typu
  • Druh typu (interface, třída, výčtový typ)
  • Anotace
  • Atributy, konstruktory, metody a vnořené datové typy

S těmito informacemi získanými za běhu programu je následně možné například volat konstruktory, metody nebo zjišťovat a přiřazovat hodnoty atributů. Lze také zjistit informace o nedostupných členech, ale v běžných situacích k nim není přístup.

Získání existujícího class-objektu

Pokud je typ daného názvu již zaveden do paměti, tak je vrácen jeho class-objekt a nevytváří se nový.

Pokud známe typ objektu a nemáme jeho instanci, pak literálem Typ.class

private Class<? extends Color> clazz = Color.class;
Field [] fields = clazz.getDeclaredFields(); //získání všech deklarovaných proměnných ze třídy

U každého objektu lze volat metodu getClass(), díky čemuž jsme schopni zjistit, jaké třídy je objekt instancí.

Trida trida = new Trida();
trida.getReflection().getAnnotations(); //získá anotace dané třídy

Získání class-objektu datového typu zadaného svým názvem

Pomocí statické metody Class.forName(String) lze získat class-objekt typu se zadaným úplným názvem, tj. včetně názvu balíčku. Při použití modulů lze volánímClass.forName(Module, String) požádat o class-objekt zadaného typu v zadaném modulu. Rozšířené možnosti nabízí metoda Class.forName(String, boolean, ClassLoader), jejíž druhý parametr zadává, zda má být daný typ inicializován, a třetí parametru specifikuje ClassLoader, který má daný datový typ zavést.

Class.forName("java.lang.String");
Class.forName("Foo",true,this.getClass().getClassLoader());

Reflexní metody třídy Class v Javě

Třída Class definuje 71 metod, z toho jen 3 statické (forName(?));

Metody pro získání konstruktorů

Statický inicializátor (tj. konstruktor třídy) není možné získat. Získat lze pouze konstruktory instancí.

Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

Metody pro získání polí

U polí se zadává jenom název.

Field getField(String name)
Field getDeclaredField(String name)

Metody pro získaní metod

Method getMethod(String name,
Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException

Všechny veřejné metody včetně zděděných

Method[] getMethods()
throws SecurityException

Všechny s výjimkou zděděných

Method[] getDeclaredMethods()
throws SecurityException
Metody pro získání názvu:
String getSimpleName()
String getName()
String toString()

Metody pro získání informací o daném druhu typu, reprezentovaném class-objektem

isAnnotation(), isAnonymousClass(), isArray(), isEnum(), isInterface(), isLocalClass(), isMemberClass(), isPrimitive(), isSynthetic()

Ostatní informace o typu

getPackage(), int getModifiers(), Class<? super T> getSuperclass(), Class<?> getComponentType()

Metody vracející reprezentanty veřejných členů

Constructor<?>[] getConstructors()
Field[] getFields()
Method[] getMethods()
Class<?>[] getClasses()
Annotation[] getAnnotations()

Metody vracející reprezentanty deklarovaných členů

Vrací soukromé členy, ale nevrací zděděné členy.

Constructor<?>[] getDeclaredConstructors()
Field[] getDeclaredFields()
Method[] getDeclaredMethods()
Class<?>[] getDeclaredClasses()
Annotation[] getDeclaredAnnotations()

Reflexe v C#

Jmenný prostor, který umožňuje v .NET používat reflexi se jmenuje System.Reflection. Základní cestou k metadatům typu v mechanismu reflexe je třída získat instanci třídy System.Type. Díky ní je možné získat rozličné informace o daném typu, včetně seznamu jeho členů. Může se tak udělat pomocí metody GetType() nebo pomocí operátoru typeof.

Type t = Type.GetType("System.Int32");
Type t2 = Type.GetType("MyNamespace.MyType", MyAssembly);
Type t3 = typeof(System.Int32);

Rozdíl mezi těmito dvěma druhy je v tom, že GetType() se vyhodnocuje za běhu, zatímco operátor typem v době kompilace. Máme-li instanci třídy Type, máme přístup k metadatům, atributům nebo vytvářet nové instance typů. Vztahy mezi dědičnosti mezi reflexními typy .NET

Ve chvíli, kdy máme odkaz na některý z těchto elementů, tak můžeme pohybovat se vztahy mezi daným elementem a souvisejícími prvky, jak je uvedeno na následujícím obrázku.

Nejzákladnějším typem reflexe je v .NET třída Type. Reprezentuje metadata pro jednotlivé deklarace typů v aplikaci. Typy obsahují členy. Ty zahrnují konstruktory, proměnné, vlastnosti, události a metody. Typy mohou navíc obsahovat vnořené typy, které se typicky používají jako pomocné třídy. Typy jsou seskupené do modulů a moduly jsou obsažené v sestavách.[3]

Reflexe v PHP

Od 5. verze tohoto jazyka, kdy se přišlo s objekty, má PHP základní schopnost reflexe, avšak ne dostatečnou, proto se různé frameworky snaží o její rozšíření. O reflexi v tomto jazyce se stará třída Reflection. V základním PHP například nemůžeme zjistit nic o anotacích daného objektu.

$a = new A();
$reflector = new ReflectionClass('A');
//vezme všechny vlastnosti třídy A a uloží je do pole
$properties = $reflector->getProperties();[4]

Nette

Nette\Object usnadňuje i přístup k sebereflexi třídy pomocí metody getReflection(), která vrací objekt třídy ClassType.

$circle = new Circle;
echo $circle->getReflection()->hasMethod('getArea'); // existuje metoda 'getArea' ?
echo $circle->getReflection()->getName(); // vrací název třídy, tj. 'Circle'[5]

Zend

Zend_Reflection_Class dědí od základní třídy Reflection a přidává další funkčnost.

$r = new Zend_Reflection_Class($class);
 printf(
    "The class level docblock has the short description: %s\n".
    "The class level docblock has the long description:\n%s\n",
    $r->getDocblock()->getShortDescription(),
    $r->getDocblock()->getLongDescription());
// Get the declaring file reflection
$file = $r->getDeclaringFile();

Reflexe v Objective-C

 // Foo třída
 @interface Foo : NSObject
 - (void)hello;
 @end
  // bez reflexe 
 Foo *obj = [[Foo alloc] init];
 [obj hello];
  // s reflexí 
 id obj = [[NSClassFromString(@"Foo") alloc] init];
 [obj performSelector: @selector(hello)];

Reflexe v Ruby

# bez reflexe
obj = Foo.new
obj.hello
# s reflexí
class_name = "Foo"
method = :hello
obj = Kernel.const_get(class_name).new
obj.send method

Reflexe v Perlu

# bez reflexe 
my $foo = Foo->new;
$foo->hello;
 # nebo
Foo->new->hello;
# s reflexí

my $class = "Foo"
my $constructor = "new";
my $method = "hello";
my $f = $class->$constructor;
$f->$method;
 # nebo
$class->$constructor->$method;

Reflexe ve Visual Basic

' Využití GetType k získání informací o daném typu:
Dim i As Integer = 42
Dim type As System.Type = i.GetType() 
System.Console.WriteLine(type) 
' Využití reflexe k získání informací z Assembly:
Dim info As System.Reflection.Assembly = GetType(System.Int32).Assembly
System.Console.WriteLine(info)
' Využití GetType k získání informací o daném typu:
Dim i As Integer = 42
Dim type As System.Type = i.GetType()
System.Console.WriteLine(type)
' Využití reflexe k získání informací z Assembly:
Dim info As System.Reflection.Assembly = GetType(System.Int32).Assembly
System.Console.WriteLine(info)

Odkazy

Reference

  1. DANEČEK, Jiří. Reflexe [online]. 2014 [cit. 2014-11-07]. Dostupné v archivu pořízeném dne 2014-11-07. 
  2. PECINOVSKÝ, Rudolf. Reflexe [online]. 2014 [cit. 2019-04-22]. Dostupné online. 
  3. BĚHÁLEK, Marek. Reflexe v C# [online]. [cit. 2014-11-07]. Dostupné v archivu pořízeném dne 2015-04-26. 
  4. The reflection class [online]. PHP.net Manual. Dostupné online. 
  5. Extension of PHP language [online]. Nette. Dostupné online.