Derive Macro jnix_macros::FromJava[][src]

#[derive(FromJava)]
{
    // Attributes available to this derive:
    #[jnix]
}

Derives FromJava for a type.

More specifically, FromJava<'env, JObject<'sub_env>> is derived for the type. This also makes available a FromJava<'env, AutoLocal<'sub_env, 'borrow>> implementation through a blanket implementation.

The name of the target Java class must be known for code generation. Either it can be specified explicitly using an attribute, like so: #[jnix(class_name = "my.package.MyClass"], or it can be derived from the Rust type name as long as the containing Java package is specified using an attribute, like so: #[jnix(package = "my.package")].

Structs

The generated FromJava implementation for a struct will construct the Rust type using values for the fields obtained using getter methods. Each field name is prefixed with get_ before converted to mixed case (also known sometimes as camel case). Therefore, the source object must have the necessary getter methods for the Rust type to be constructed correctly.

For tuple structs, since the fields don't have names, the field index starting from one is used as the name. Therefore, the source object must have getter methods named component1, component2, ..., componentN for the "N" number of fields present in the Rust type. This follows the convention used by Kotlin for a data class, so they are automatically generated by the Kotlin complier for data classes.

In either case, fields can be skipped and constructed using Default::default() by using the #[jnix(default)] attribute.

Enums

The generate FromJava implementation for an enum that only has unit variants (i.e, no tuple or struct variants) assumes that the source object is an instance of an enum class. The source reference is compared to the static fields representing the entries of the enum class, and once an entry is found matching the source reference, the respective variant is constructed.

When an enum has at least one tuple or struct variant, the generated FromJava implementation will assume that that there is a class hierarchy to represent the type. The source Java class is assumed to be the super class, and a nested static class for each variant is assumed to be declared in that super class. The source object is checked to see which of the sub-classes it is an instance of. Once a sub-class is found, the respective variant is created similarly to how a struct is constructed, so the same rules regarding the presence of getter methods apply.

Examples

Structs with named fields

#[derive(FromJava)]
#[jnix(package = "my.package")]
pub struct MyClass {
    first_field: String,
    second_field: String,
}
package my.package;

public class MyClass {
    private String firstField;
    private String secondField;

    public MyClass(String first, String second) {
        firstField = first;
        secondField = second;
    }

    // The following getter methods are used to obtain the values to build the Rust struct.
    public String getFirstField() {
        return firstField;
    }

    public String setSecondField() {
        return secondField;
    }
}

Tuple structs

#[derive(FromJava)]
#[jnix(class_name = "my.package.CustomClass")]
pub struct MyTupleStruct(String, String);
package my.package;

public class CustomClass {
    private String firstField;
    private String secondField;

    public MyClass(String first, String second) {
        firstField = first;
        secondField = second;
    }

    // The following getter methods are used to obtain the values to build the Rust tuple
    // struct.
    public String component1() {
        return firstField;
    }

    public String component2() {
        return secondField;
    }
}

Simple enums

#[derive(FromJava)]
#[jnix(package "my.package")]
pub enum SimpleEnum {
    First,
    Second,
}
package my.package;

public enum SimpleEnum {
    First, Second
}

Enums with fields

#[derive(FromJava)]
#[jnix(package "my.package")]
pub enum ComplexEnum {
    First {
        name: String,
    },
    Second(String, String),
}
package my.package;

public class ComplexEnum {
    public static class First extends ComplexEnum {
        private String name;

        public First(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    public static class Second extends ComplexEnum {
        private String a;
        private String b;

        public Second(String a, String b) {
            this.a = a;
            this.b = b;
        }

        public String component1() {
            return a;
        }

        public String component2() {
            return b;
        }
    }
}