6.68. kink/javahost/JAVA_PROXY

6.68.1. JAVA_PROXY.new(Interface $handler ...[$config={}])

`new` makes a Java object which delegates actions of its methods to a Kink function $handler.

Config methods:

• C.also_implement(Also_implement): default = []

• C.override_tostring

• C.override_equals

• C.override_hashcode

• C.override_default

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = a function which raises an exception

The created Java object will be an instance of java.lang.reflect.Proxy, which implements `Interface` and interfaces in `Also_implement`.

If proxy creation succeeds, `new` tail-calls $success with a `java` value (proxy, Interface), where `proxy` is the created instance.

If proxy creation fails with IllegalArgumentException, as described in the specification of Proxy.newProxyInstance, `new` tail-calls $error with a `java` value (iae, IllegalArgumentException), where `iae` is the thrown IllegalArgumentException.

Invocation

Invocations of proxy methods, except for those described later, are delegated to $handler.

When a method is invoked, a new abstract stack machine is created, then $handler is called with the java.lang.reflect.Method object, and a `vec` of the arguments of the Java method, in the abstract stack machine.

The first argument of $handler is a `java` value (method, java.lang.reflect.Method), where `method` is the java.lang.reflect.Method object of the invoked method.

The second argument of $handler is a `vec` of `java` values (arg, argType). Here, `argType` is the corresponding parameter type of the method. If `argType` is a reference type, `arg` is the passed argument itself. If `argType` is a primitive type, `arg` is a wrapper instance which wraps the argument.

Invocation of toString

If C.override_tostring is not called, Object.toString() method of the proxy returns a string like "java-kink-proxy(java.lang.Runnable)".

If C.override_tostring is called, the invocation of toString() is delegated to $handler.

Invocation of equals

If C.override_equals is not called, Object.equals(Object) method of the proxy tests the identity using Java references by Java == operator.

If C.override_equals is called, the invocation of equals(Object) is delegated to $handler.

Invocation of hashCode

If C.override_hashcode is not called, Object.hashCode() method of the proxy returns a result of System.identityHashCode().

If C.override_hashcode is called, the invocation of hashCode() is delegated to $handler.

Invocation of interface default methods

If C.override_default is not called, interface default methods of the proxy delegate the invocations to the implementations of the default methods.

If C.override_default is called, the invocations are delegated to $handler.

To return from the invocation

If the return type of the method is a reference type, to order that the invocation return a value `ref` of the reference type, $handler must return a `java` value (ref, *).

If the return type of the method is char, boolean, byte, short, int, long, float, or double, to order that the invocation return a value `primitive`, $handler must return a `java` value (wrapper, *), where `wrapper` is a wrapper instance object which wraps `primitive`.

If the return type of the method is void, to order that the invocation return, $handler must return any value other than `java_throw`.

To throw a Java Throwable from the invocation

To order that the invocation throw a Java Throwable, $handler must return a `java_throw`.

Preconditions

`Interface` must be a `java` value (cl, *), where `cl` is an instance of java.lang.Class.

`Also_implement` must be a `vec` of `java` values (cl, *), where `cl` is an instance of java.lang.Class.

$success must be a function which takes a `java` value.

$error must be a function which takes a `java` value of an IllegalArgumentException.

Example: Make a proxy instance

:JAVA.require_from('kink/javahost/')
:JAVA_PROXY.require_from('kink/javahost/')

:IntUnaryOperator_class <- JAVA.class('java.util.function.IntUnaryOperator')

:Increment <- JAVA_PROXY.new(IntUnaryOperator_class){(:Method :Args)
  Method.call_method('getName').to_kink_str == 'applyAsInt' || raise('unexpected method')
  JAVA.int(Args.front.to_kink_num + 1)
}

:Double <- JAVA_PROXY.new(IntUnaryOperator_class){(:Method :Args)
  Method.call_method('getName').to_kink_str == 'applyAsInt' || raise('unexpected method')
  JAVA.int(Args.front.to_kink_num * 2)
}

:Increment_then_double <- Increment.call_method('andThen' Double) # call default implementation
:R <- Increment_then_double.call_method('applyAsInt' JAVA.int(3))
stdout.print_line(R.repr) # => (java "8" as int)

Example: Override toString

:JAVA.require_from('kink/javahost/')
:JAVA_PROXY.require_from('kink/javahost/')

:IntUnaryOperator_class <- JAVA.class('java.util.function.IntUnaryOperator')
:Object_class <- JAVA.class('java.lang.Object')

:Increment <- JAVA_PROXY.new(IntUnaryOperator_class
  {(:Method :Args)
    :Method_name = Method.call_method('getName').to_kink_str
    branch(
      { Method_name == 'toString' } {
        JAVA.string('IntUnaryOperator(increment the int)')
      }
      { Method_name == 'applyAsInt' } {
        JAVA.int(Args.front.to_kink_num + 1)
      }
      { true } {
        Method_name == 'applyAsInt' || raise('unexpected method')
        JAVA.int(Args.front.to_kink_num + 1)
      }
    )
  }
  {(:C) C.override_tostring }
)

stdout.print_line(Increment.call_method('applyAsInt' JAVA.int(3)).repr)
# => (java "4" as int)

stdout.print_line(Increment.as(Object_class).call_method('toString').to_kink_str.repr)
# => "IntUnaryOperator(increment the int)"