(rontolisp) docs
← java Package Functions

java:proxy

(java:proxy "fully.qualified.Interface"... callable)

Creates a host instance of the given interfaces -- one or more -- backed by a rontolisp callable. Every interface method is dispatched to the callable as (callable "method-name" arg...) — so the callable's first argument is the name of the invoked method (a string) and the remaining arguments are the method's arguments. The callable's return value is marshalled back to the method's return type (a void method ignores it; a function returned is not made a proxy -- return a java:proxy or java:reify object where an interface is expected). This is how a rontolisp lambda becomes a Java listener or comparator. For a single-method (SAM) interface the method name is always the same, so it is conventionally ignored (the method parameter in the examples below). Part of the JVM-only java interop package — available on the interpreter and in JVM-compiled classes, not on the WASM backend. See the Java interop guide.

A java.util.function.Supplier is implemented by the lambda; calling its get method runs the lambda and returns 42.

Functional interfaces

Any interface works, including the java.util.function family. The lambda's first parameter receives the method name; the rest receive the method arguments, so match the arity to the interface's single abstract method (SAM):

InterfaceSAMLambda shape
Supplierget()(lambda (method) ...)
Functionapply(x)(lambda (method x) ...)
Consumeraccept(x)(lambda (method x) ...)
Predicatetest(x)(lambda (method x) ...)
BiFunctionapply(a, b)(lambda (method a b) ...)
BinaryOperatorapply(a, b)(lambda (method a b) ...)
Comparatorcompare(a, b)(lambda (method a b) ...)

The proxy also works when the JDK itself invokes the SAM method. For example HashMap.merge calls the supplied BiFunction to combine the old and new value:

Default methods

A dynamic proxy routes every method call to the callable, including default methods such as BiFunction.andThen or Predicate.and. Calling one dispatches to the lambda as (callable "andThen" ...) rather than running the interface's built-in default implementation, so combinators like (f.andThen g) are not available — call the single abstract method (apply/test/accept/get/ compare) instead.

To implement each method with a function of its own -- a default method then keeping its body -- use java:reify.

Java's false

Java's false reaches the callable as nil. Ending the form in :java-false, after the callable, hands it |false| instead (the guide's Java's false back):

Ending it in :octets hands the callable a byte[] -- an argument, or an element of one -- as an (unsigned-byte 8) vector of its octets -- Java's own array, so what the callable stores Java reads (the guide's Octets back). Ending it in :through and a function has Java call the callable through that function, the method's name first among the arguments it is handed (the guide's Calling functions through another).

Several interfaces

Every name before the callable is an interface the one object implements, so Java can hold it as any of them. A method name two interfaces declare reaches the callable as that one name, whichever interface Java calls it through:

Each name must be an interface, and named once (java:proxy names interface I twice). The object's toString is #<java-proxy I J>. A call on the object itself, such as (java:call p "accept" 1), is resolved by its class when it runs; passing it where one of its interfaces is expected resolves before it runs.

In a compiled program

A java:proxy whose interface is a literal string the compile can see is a class generated at compile time (Prog$Proxy0.class beside the program), as is a function passed where an interface is expected: no java.lang.reflect.Proxy, so the program compiles under --java-static and builds into a GraalVM native image with no configuration. It prints as #<java Prog$Proxy0>, where the interpreter prints the name of a java.lang.reflect.Proxy class. A java:proxy of an interface named at run time goes through the reflection bridge.