This is meant to be a quick guide on how to use JsInterop.
For calling browser APIs you may want to consider Elemental which has autogenerated and accurate JsInterop abstractions for (common) JavaScript APIs.
@JsType
- Interface with an extern type.
Note that @JsType
is just syntactical sugar for applying @JsConstructor
,
@JsMethod
or@JsProperty
to all public constructors, methods and properties
respectively.
@JsType(isNative=true, namespace=JsPackage.GLOBAL)
class RegExp {
public native RegExp(String pattern, String... flags);
public native boolean test();
public String flags;
}
// Usage
static void main() {
RegExp re = new RegExp("asdf", "g");
String flags = re.flags; // "g"
re.test("hello");
}
// JsInterop for goog.string.linkify
class ClosureLinkify {
@JsMethod(namespace="goog.string.linkify")
public static native String findFirstEmail(String text);
@JsMethod(namespace="goog.string.linkify")
public static native String findFirstUrl(String text) {}
}
Example usage:
// Main.java
ClosureLinkify.findFirstEmail("this is an email: aaa@gmail.com");
// Main.java.js (translated).
const Linkify = goog.require("goog.string.linkify");
Linkify.findFirstEmail("this is an email: aaa@gmail.com");
class Main {
@JsFunction
interface Callback {
void run();
}
@JsMethod(namespace=JsPackage.GLOBAL)
private static native int setTimeout(Callback callback, int delayMs);
public static void main() {
setTimeout(() -> {
// do something!
}, 1000);
}
}
// Existing ClosureEnum.js
/** @enum {string} */
const ClosureEnum = {
OK : 'Ok',
CANCEL : 'Cancel'
}
@JsEnum(isNative=true)
enum ClosureEnum {
OK,
CANCEL
}
You can add String value
field and set hasCustomValue=true
if you would
like get the underlying String value of the enum.
Java transpiled by J2CL is not directly callable from JavaScript by default. J2CL Java classes have mangled method and property names to preserve Java semantics in the translated code.
Mangled names are not public APIs for Java classes and the mangling scheme is subject to change. Instead, JsInterop annotations are the safe way to provide JavaScript APIs.
// MyClass.java
package com.google.example;
class MyClass {
@JsConstructor
public MyClass() {}
}
goog.module("example.module");
const MyClass = goog.require("com.google.example.MyClass");
new MyClass();
// Interop.java
package com.google.example;
class Interop {
@JsMethod
public void jsMethod() {}
// Cannot call this from JS.
public void notJsMethod() {}
}
// Generated Interop.java.js
goog.module("com.google.example.Interop");
class Interop {
jsMethod() {}
// This is mangled. Don't call it. The name is subject to change.
m_notJsMethod__() {}
}
exports = Interop;
// Interop.java
package com.google.example;
public class Interop {
@JsProperty
public static final int staticProp = 10;
@JsProperty
public int instanceProp = 30;
@JsMethod
public static Interop getInstance() {
return new Interop();
}
}
// usage.js
goog.module("com.google.example.Usage");
const Interop = goog.require("com.google.example.Interop")
console.log(Interop.staticProp); // 10
console.log(Interop.getInstance().instanceProp); // 30
See Optional Types in Closure Compiler.
// Main.java
public class Main {
@JsMethod
public void method1(@JsOptional Double i) {}
}
// Main.java.js (transpiled)
class Main {
/**
* @param {number=} d // Note the "=" here.
* @return {void}
* @public
*/
method1(i, d) {}
}
// Exported.java
package com.google.example;
@JsType
public class Exported {
public int prop;
public Exported() {
this.prop = 100;
}
public int getProp() {
return this.prop;
}
}
// usage.js
goog.module("com.google.example.usage");
const Exported = goog.require("com.google.example.Exported");
let e = new Exported();
console.log(e.prop); // 100
console.log(e.getProp()); // 100
@JsEnum
enum ExposedEnum {
VALUE1,
VALUE2;
void someMethod();
}
// ExposedEnum.impl.java.js (transpiled)
/** @enum {number} */
const ExposedEnum = {
VALUE1 : 0,
VALUE2 : 1
}
// Note that no methods from original Java source are exposed here.
// Main.java
public class Main {
@JsMethod
public void method(@JsOptional Object o) {
Console.log("Called method() with " + o);
}
@JsMethod
public native void method() {}
private void main() {
method("Hello"); // logs to console "Called method() with Hello"
method(); // logs to console "Called method() with null"
}
}
// SubMain.java
public class SubMain extends Main {
@JsMethod
public void method(@JsOptional Object o1, @JsOptional Object o2) {
Console.log("Called method(Object, Object) with " + o1 + " " + o2);
}
// Override the original implementation with a native method to make sure
// there is still single implementation of the method in the class.
@JsMethod
public native void method(@JsOptional Object o);
private void main() {
method("Hello", "GoodBye"); // logs to console "Called method() with Hello GoodBye"
method("Hello"); // logs to console "Called method() with Hello null"
method(); // logs to console "Called method() with null null"
}
}
@JsEnum(hasCustomValue=true)
enum ExposedEnum {
VALUE1("Value1"),
VALUE2("Value2");
// An instance field named 'value' defines the custom value type.
String value;
// A single constructor with this shape is needed to define custom values.
ExposedEnum(String value) {
this.value = value;
}
}
// ExposedEnum.impl.java.js (transpiled)
/** @enum {?string} */
const ExposedEnum = {
VALUE1 : 'Value1',
VALUE2 : 'Value2'
}