Skip to content

Custom references

Most of the time working with PraxisCORE you do not need to manage references to objects manually. However, Ref<T> offers a way to hook into the reference handling lifecycle inside the runtime when this is required eg. for interacting with other libraries. It provides a safe way for carrying references across code changes, and determining what happens when code is updated or the reference deleted.

The reference must be initialized before use, often with a constructor references. Handlers can be added for onReset() and onDispose() - the former will be called every code change, the latter when the root is stopped or the reference deleted. If the reference is AutoCloseable then it will be closed automatically on disposal by default.

Use eg.

@Inject Ref<List<String>> strings;

public void init() {
    strings.init(ArrayList::new)
           .apply(l -> {
                if (l.isEmpty()) {
                    l.add("A String");
                }
            });
    List<String> lst = strings.get(); // same list across code changes
}

See the Javadoc for Ref<T> included with the IDE for the full range of features.

Ref.Provider

As well as using Ref<T> directly in components, a reference provider can be registered to handle the configuration of multiple references from one place. The @Inject annotation supports a provider value. The provider implementation can be a static class or separately in shared code to manage references across a graph. The field itself should be of the injected type.

There is also a default reference provider, that allows injecting fields of List, Set and Map which will inject ArrayList, LinkedHashSet and LinkedHashMap respectively.

package SHARED;
// imports

public class Types extends Ref.Provider {
    public Types() {
        register(List.class, (Ref<List<?>> r) ->
                r.init(LinkedList::new).onReset(List::clear));
    }
}
@Inject List<String> arrayList;
@Inject(provider = SHARED.Types.class) List<String> linkedList;

Reference ports

A Ref<T> field can be annotated with @Out to create a reference output port. Input ports use Ref.Input<T>, which provides a List<T> of values from connected references.

@Out Ref<String> out;
@In Ref.Input<String> in;

public void init() {
    List<String> values = in.values();
    in.onUpdate(list -> {
        if (!list.isEmpty()) {
            // do something with inputs
        }
    });
}

Publish and Subscribe

A Ref<T> field in a container can be marked with @Publish annotation. Any Ref<T> fields in direct child components can be annotated with @Subscribe and will automatically update in sync with the container value. Both annotations support an optional name.

@Inject @Ref.Publish
Ref<String> defRef;

@Inject @Ref.Publish(name = "alt")
Ref<String> altRef;

public void init() {
    defRef.set("default");
    altRef.set("alternate");
}
@Inject @Ref.Subscribe
Ref<String> defRef;

@Inject @Ref.Subscribe(name = "alt")
Ref<String> altRef;

public void init() {
    String foo = altRef.orElse("");
    // foo == "alternate"
}

Async set

It's possible to set the value of a Ref<T> field using an Async task. Care should be taken not to capture state of the component in any background task.