Java

Calling Java functions

Consider a typical Java "Hello, world" program:

import java.lang.System;

class Main {
	public static void main(String[] args) {
		System.out.println("Hello, world!");
	}
}

To translate it directly to Keen:

import java/io/PrintStream java/lang/System main void() java, global trusted println out, "Hello, world!"

An import beginning with java/ imports a class from the Java standard library.
(Replace . with /. So java/io/PrintStream imports java.io.PrintStream .)

Keen is not Java and doesn't have many of Java's features, such as classes and methods.
When Keen imports a Java class, the class' public properties and methods are translated to functions.

  • A static field becomes a function with no arguments. The class name is omitted.
    For example, Java's System.out becomes out in Keen.
  • A static method can be called directly. The class name is omitted.
    For example, Java's Math.sqrt becomes sqrt in Keen.
  • An instance field becomes a function taking the class as an argument.
    For example, Java's Point.x instance field becomes a function x in Keen.
  • For an instance method, a this argument of the class' type is prepended to the arguments.
    For example, the instance method println takes a java/io/PrintStream (such as out) as its first argument.
  • A class constructor becomes a function named new, and a function named the same as the class. (Use whichever you prefer.)

All functions that come from Java have the java, global, and unsafe specs.

In Java, methods on a class can be called anywhere without importing the class. Keen doesn't work like that; if you want to call a method, you need to import the class.
The Java program above only needed import java.lang.System;, while the Keen version needs to import both java/io/PrintStream and java/lang/System.

Null values

In Java, all reference types have a null value.
Keen considers null values to be unsafe, so don't don't mark a call to a function that comes from Java as trusted unless you are sure it won't return null.

java-null is an unsafe function returning a Java null value for a type.
As a convenience, is-null a is equivalent to reference-equal a, java-null.

The below example reads a directory, handling the case where it does not exist.

import keen/java java/lang/String java/io/File main void() java, global log get-files "some-dir" get-files string[](dir-name string) java, global trusted dir File = dir-name.to::String, files String buffer = list dir forbid is-null files else error "Directory does not exist" for file in files to::string file

Casting

Java subclasses implicitly convert to their superclasses (and interfaces).

For reference types, reinterpret-cast corresponds to a Java cast.
This can be used to convert a Java class to a subclass.

import keen/unsafe java/lang/Object java/lang/String main void() java, global, unsafe a String = "hello" b Object = a c String = reinterpret-cast b log c.to

Generics

Keen supports Java generic types such as List.

import java/lang/Iterable java/lang/String java/util/Iterator java/util/Arrays java/util/List keen/java main void() java, global, unsafe buf String buffer = "hello", "world" list String List = asList buf iterable String Iterable = list iterator String Iterator = iterator iterable log to::string next iterator log to::string next iterator log hasNext iterator

Type translation

Java types translate to the following Keen types:

JavaKeen
voidvoid
boolbool
byteint8
shortint16
intint32
longint
floatfloat32
doublefloat64
t[]t buffer

Since Java Arrays are mutable, they are translated as buffer instead of array.
If a Java method treats an Array argument as read-only. you could use cast-mutable to pass in a Keen array.
If a Java method returns an Array that will never be mutated, you could use cast-immutable to get it as a Keen array.

import java/io/PrintStream java/lang/String java/lang/System main void() java, global, unsafe bytes int8[] = 104, 101, 108, 108, 111 # cast-mutable because 'new String()' does not mutate its argument s String = bytes.cast-mutable, println out, s

There are additional functions in keen/unsafe for converting between types that have the same runtime representation, such as char8 buffer and nat8[]. (These work in any target, not just Java.)

Implementing Java interfaces

Keen record types can implement Java interfaces directly. It works just like implementing a Keen interface.

import java/lang/Object java/util/Arrays java/util/Comparator main void() global, java, unsafe a point buffer = - 1, 2 - 2, 0 - 1, 1 # sort is from 'java/lang/Arrays' sort a, point-comparator log a point record x nat y nat point-comparator record() point Comparator case equals bool(_ point-comparator, _ Object) # Comparator requires this, but it won't actually be used throw unreachable compare int32(_ point-comparator, a point, b point) match a <=> b as less then -1 as equal then 0 as greater then 1

Keen doesn't make use of Java generics, so:

  • Java's sort function acts on an Object buffer .
  • The interface is just Comparator with no type parameter.
  • The comparator accepts Object parameters and downcasts them using reinterpret-cast.

Nested classes

A nested class like Map.Entry is imported as Map$Entry. The name of the type is just Entry.

import java/util/Iterator java/util/Map java/util/Map$Entry java/util/Set main void() global, java, unsafe map (nat, string) Map = ofEntries - entry 1, "one" - entry 2, "two" iterator (nat, string) Entry Iterator = iterator entrySet map entry = next iterator log entry.getKey, entry.getValue

Keen types

There are no guarantees about how Keen translates to Java.
A Keen interface isn't always a Java interface.
A Keen record isn't always a Java record.
So don't expect to use Java reflection.

JARs

To use Java classes from outside the standard library, they must be in a JAR file.
The imports section of config.kid supports JAR files. (See "config.kid".)
For example:

imports: "org/foo": "./libs/foo.jar"

With the above configuration, all imports starting with org/foo will resolve to classes from libs/foo.jar.

import # Gets 'org/foo/Foo.class' from 'libs/foo.jar' org/foo/Foo main void() global, java, unsafe someStaticMethod

When you build the program, all classes (even ones not directly referenced) from configured JAR files will be copied to the output, so it builds to a standalone executable JAR.