18 — Advanced
Type References
Java erases generic type information at runtime. TypeReference solves this by using the anonymous subclass pattern to capture and preserve generic type information, so rulii can work with fully parameterized types like List<String> or Map<String, Integer>.
The Problem
Without TypeReference, generic type information is lost at runtime:
// Without TypeReference — type info is lost
bindings.bind("tags", new ArrayList<String>());
// Binding type is just ArrayList, not ArrayList<String>
The Solution
Use TypeReference to preserve the full generic type:
// With TypeReference — generic type is preserved
bindings.bind("tags", new TypeReference<List<String>>() {}, new ArrayList<>());
// Binding type is List<String>
// Check the captured type
Binding<List<String>> binding = bindings.getBinding("tags");
Type type = binding.getType(); // java.util.List<java.lang.String>
When to Use TypeReference
Use TypeReference whenever you need type-safe bindings with generic types:
List<T>
Preserve element type for lists
Map<K, V>
Preserve key and value types for maps
Optional<T>
Preserve wrapped type for optionals
Custom Generics
Any parameterized type you define
Programmatic Type Creation
Use TypeReference.with(Type) when you already have a Type instance and need to wrap it in a TypeReference:
// Factory method for programmatic type creation
Type existingType = /* obtained via reflection */;
TypeReference<?> ref = TypeReference.with(existingType);
Integration
TypeReference integrates with the rest of rulii's type system. When bindings have accurate generic type information, the framework can perform correct parameter matching and automatic type conversion, ensuring that values are passed to conditions, actions, and functions with the right types.