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.