Advanced topics Reflection

Reflection

Reflection in Klyn starts with type(...) and expands into metadata about members, inheritance, annotations, generics, and more. This is especially useful for tooling, tests, and framework-style code.

Acquiring a Type

import klyn.math

r = Rational(1, 3)
metadata = type(r)
print(metadata.name)
print(metadata.fullName)

type(expression) evaluates its value argument exactly once, including any function calls or property getters, even when the result type is known statically. Unlike sizeof, it is not an unevaluated operation.

Kind Flags

interface Worker:
    public run() as Void

metadata = type(Worker)
assert metadata.isInterface
assert metadata.isAbstract

Reflection exposes whether a type is public, abstract, final, native, an interface, an enum, or an annotation.

Inspecting Members

import klyn.math

metadata = type(Rational(1, 3))
attrs = metadata.getAttributes()
props = metadata.getProperties()
methods = metadata.getMethods()

You can inspect declared members, retrieve a member by name, and choose whether inherited members should be included.

Dynamic Invocation

import klyn.reflection

method = Application.type.getMethod("time")
if method is Invocable<Object>:
    result = method.invoke(Object[0])
    print(result)

Invocable<R> is the common reflection-level contract for functions obtained dynamically, methods, and constructors. Its arguments use Object[] because their signature is runtime metadata. For ordinary callbacks and lambda parameters, prefer the complete structural form (P1, P2) -> R, which the compiler can validate statically. The two models are intentionally distinct and no implicit conversion erases a structural signature into an Invocable<R>.

Property Capabilities

class CredentialSink:
    public writeonly property credential as String

sink = CredentialSink()
property = type(sink).getProperty("credential")

assert property.isWriteonly
assert not property.isReadonly
property.setValue(sink, "temporary secret")

Reflected properties preserve their source contract. isReadonly identifies a getter-only property and isWriteonly identifies a setter-only property. Calling setValue() on a readonly property or getValue() on a write-only property raises TypeError; reflection never bypasses accessor visibility.

Generic Reflection

import klyn.collections

metadata = type(List<Int>)
print(metadata.isGenericInstantiation)
print(metadata.genericParameterCount)
print(metadata.genericParameterNames())

Annotations Through Reflection

annotation Audited:
    pass

metadata = type(Audited)
print(metadata.isAnnotation)

Reflection can also expose annotations attached to types and members, which is how test frameworks and metadata-driven tooling discover annotated declarations.

A positional annotation argument supplies its value property. An annotation can accept several positional values by declaring a variadic value constructor; the property then exposes the argument list. The element types are checked by the compiler, just like ordinary variadic arguments.

import klyn.collections

annotation Labels:
    public readonly property value as IList<String>

    public Labels(value ... as String):
        this.value = value

@Labels("billing", "reporting")
class Invoice:
    pass

@Labels() supplies an empty list. Positional and named annotation arguments cannot be mixed. For data-driven tests, see @Case and @Cases.