摘要:例如允许的对象默认情况下,通过使用内置宏将核心对象和方法映射到。例如这被转换为以下代码类可以定义构造函数,具有超类,并且可以像在中一样实例化。因此,它不违反原则。用于声明该对象可以用作构造函数。
这个工具可以将java代码转为js代码,从而可以使用java编写前端代码 如果排版看着费劲可以下载下方html,打开html后使用google翻译
JSweet语言规范
版本:2.x(快照)
作者:Renaud Pawlak
作者助理:Louis Grignon
JSweet JavaDoc API:http://www.jsweet.org/core-api-javadoc/
注意:此降价是从Latex源文件自动生成的。不要直接修改。
内容
基本概念
核心类型和对象
类
接口
无类型对象(地图)
枚举
全局
可选参数和重载
桥接外部JavaScript元素
例子
写定义规则(又名桥梁)
无法访问
混入
从现有的TypeScript定义生成JSweet糖果
辅助类型
功能类型
对象类型
字符串类型
元组类型
联盟类型
交叉类型
语义
主要方法
初始化器
数组初始化和分配
异步编程
姓名冲突
测试对象的类型
lambda表达式中的变量作用域
这个范围
打包
使用您的文件,无需任何包装
为浏览器创建捆绑包
包装模块
根包
包装JSweet jar(糖果)
扩展转换器
核心注释
集中注释 jsweetconfig.json
使用适配器进行编程调整
扩展示例
附录1:JSweet转换器选项
附录2:包装和静态行为
调用main方法时
静态和继承依赖项
基本概念
本节介绍JSweet语言的基本概念。必须记住,JSweet作为Java-to-JavaScript转换器,是编译时Java的扩展,并在运行时作为JavaScript执行。JSweet旨在通过尽可能多地尊重Java语义来实现Java和JavaScript之间的权衡,但不会失去与JavaScript的互操作性。因此,在某种程度上,JSweet可以看作是Java和JavaScript之间的融合,试图用一种独特且一致的语言来充分利用这两个世界。在某些情况下,很难充分利用这两个世界,JSweet可以提供方便实用的选择。
因为JSweet是一个开放的JavaScript转换器,所以用户可以毫不费力地调整JavaScript生成,从而做出除默认选择之外的其他选择来将Java映射到JavaScript。例如,如果JSweet实现Java映射的方式不适合您的上下文或用例,则可以编写JSweet扩展以覆盖默认策略。第6节详细介绍了编程和激活JSweet扩展 。
核心类型和对象
JSweet允许使用原始Java类型,核心Java对象(在java.lang许多JDK类中定义(特别是java.util但不仅仅是),以及在def.js包中定义的核心JavaScript对象 。接下来,我们描述了这些核心类型的使用和对象。
原始Java类型
JSweet允许使用Java原始类型(和相关的文字)。
int,byte,short,double,float被全部转换成JavaScript数字(打字稿number类型)。精密通常不会在JSweet无所谓,但是,铸造int,byte或 short强制四舍五入到合适的长度整数数量。
char遵循Java类型规则,但由转换器转换为JavaScript string。
boolean对应于JavaScript boolean。
java.lang.String对应于JavaScript string。(不是说原始类型,但是是不可变的并且在Java中用作字符串文字的类)
转换的直接后果是,JSweet中并不总是可以使用数字或字符/字符串安全地重载方法。例如,方法pow(int, int)以及 pow(double, double)可能引起过载的问题。使用JSweet上下文,转换器将能够选择正确的方法,但JavaScript互操作性可能是一个问题。总之,由于没有区别n instanceof Integer和n instanceof Double (它既装置typeof n === ’number’)调用pow(number, number) 从JavaScript将随机选择一个实现或其他。这不应该总是一个问题,但在某些特殊情况下,它可能会产生微妙的错误。请注意,在这些情况下,程序员将能够调整JavaScript生成,如第6节中的完整解释 。
有效声明的示例:
// warning "==" behaves like JavaScript "===" at runtime
int i = 2;
assert i == 2;
double d = i + 4;
assert d == 6;
String s = "string" + "0" + i;
assert s == "string02";
boolean b = false;
assert !b;
该==运营商的行为类似于JavaScript的严格等于运算符 ===,使其接近Java语义。同样,!=映射到!==。在将对象与null文字进行比较时,该行为有一个例外。在这种情况下,JSweet转换为松散的相等运算符,以便程序员看不到null和之间的区别undefined(这在JavaScript中是不同的,但它可能会让Java程序员感到困惑)。要控制JSweet是否生成严格或松散的运算符,可以使用以下辅助方法:jsweet.util.Lang.$strict和 jsweet.util.Lang.$loose。在这样的宏中包装比较运算符将强制JSweet生成严格或松散的运算符。例如:
import static jsweet.util.Lang.$loose;
[...]
int i = 2;
assert i == 2; // generates i === 2
assert !((Object)"2" == i);
assert $loose((Object)"2" == i); // generates "2" == i
允许的Java对象
默认情况下,JSweet通过使用内置宏将核心Java对象和方法映射到JavaScript。这意味着Java代码直接替换为实现类似行为的有效JavaScript代码。为大多数有用的核心Java类(java.lang,java.util)实现了默认映射。在可能的情况下(当它有意义时),为其他JDK类实现一些部分映射,例如输入和输出流,区域设置,日历,反射等。
使用默认行为,我们可以指出以下限制:
除了某些特定的上下文之外,通常不可能扩展JDK类。如果需要扩展JDK类,应该考虑重构您的程序,或者使用允许它的JavaScript运行时(例如J4TS)。
Java反射API(java.lang.reflect)仅限于非常基本的操作。可以访问类和成员,但无法访问类型。可以使用更完整的Java反射支持,但需要JSweet扩展。
目前还不支持Java 8流,但部分支持它们很简单(欢迎贡献)。
有效声明的示例:
Integer i = 2;
assert i == 2;
Double d = i + 4d;
assert d.toString() == "6";
assert !((Object) d == "6");
BiFunction
assert "bc" == f.apply("abc", 1);
获得更多Java API
使用JSweet,可以添加在JavaScript中实现Java API的运行时,以便程序员可以访问更多Java API,从而在Java和JavaScript之间共享相同的代码。为JSweet实现Java API的核心项目是J4TS(https://github.com/cincheo/j4ts),它包含一个非常完整的java.util.*类和其他核心包的实现。J4TS基于GWT的JRE仿真的一个分支,但它适合用JSweet编译。程序员可以将J4TS用作Maven存储库中的常规JavaScript库。
虽然J4TS不能直接执行Java核心类型的使用JavaScript的人发生冲突(Boolean,Byte,Short,Integer, Long,Float,Double,Character,String),J4TS有助于通过为每个类提供佣工(支持他们的静态部分javaemul.internal.BooleanHelper,javaemul.internal.ByteHelper...)。当JSweet转换器在java.lang.T不支持作为内置宏的类型上遇到静态Java方法时 ,它会委托给 javaemul.internal.THelper,它可以为给定的静态方法提供JavaScript实现。这样,通过使用J4TS,程序员可以使用更多的核心JRE API。
Java数组
数组可以在JSweet中使用,并转换为JavaScript数组。数组初始化,访问和迭代都是有效的语句。
int[] arrayOfInts = { 1, 2, 3, 4};
assert arrayOfInts.length == 4;
assert arrayOfInts[0] == 1;
int i = 0;
for (int intItem : arrayOfInts) {
assert arrayOfInts[i++] == intItem;
}
核心JavaScript API
核心JavaScript API已定义def.js(完整文档可在http://www.jsweet.org/core-ap...)。主要的JavaScript类是:
def.js.Object:JavaScript Object类。JavaScript对象函数和属性的共同祖先。
def.js.Boolean:JavaScript布尔类。布尔值的包装器。
def.js.Number:JavaScript Number类。数值的包装器。
def.js.String:JavaScript String类。字符串的包装器和构造函数。
def.js.Function:JavaScript函数类。函数的构造函数。
def.js.Date:JavaScript Date类,它支持基本存储和检索日期和时间。
def.js.Array
def.js.Error:JavaScript错误类。这个类实现 java.lang.RuntimeException并且可以被抛出并被try ... catch语句捕获。
使用JavaScript框架时,程序员应该在大多数时间使用此API,这与HTML5兼容并遵循JavaScript最新支持的版本。但是,对于需要与Java文字(数字,布尔值和字符串)一起使用的对象,java.lang 建议使用包类。例如,jQuery API声明 $(java.lang.String)而不是$(def.js.String)。这允许程序员使用文字来编写表达式,例如$("a")(用于选择文档中的所有链接)。
使用JSweet,程序员可以根据需要轻松地从Java切换到JavaScript API(反之亦然)。在jsweet.util.Lang 类定义方便的静态方法投来回核心Java对象到其相应的JavaScript对象。例如,该 string(...)方法将允许程序员从Java切换到JavaScript字符串,反之亦然。
import static jsweet.util.Lang.string;
// str is a Java string, but is actually a JavaScript string at runtime
String str = "This is a test string";
// str is exactly the same string object, but shown through the JS API
def.js.String str2 = string(str);
// valid: toLowerCase it defined both in Java and JavaScript
str.toLowerCase();
// this method is not JS-compatible, so a macro generates the JS code
str.equalsIgnoreCase("abc");
// direct call to the JS substr method on the JavaScript string
string(str).substr(1);
// or
str2.substr(1);
注意:例如,对于JavaScript客户端和Java服务器之间的代码共享,最好只使用Java API并避免使用JavaScript API。JavaScript API将编译有效的Java字节码,但尝试在JVM上执行它们会引起不满意的链接错误。
这是另一个示例,显示了使用该array方法访问pushJavaScript数组中可用的方法。
import static jsweet.util.Lang.array;
String[] strings = { "a", "b", "c" };
array(strings).push("d");
assert strings[3] == "d";
类
JSweet中的类完全支持所有类型的Java类声明。例如:
public class BankAccount {
public double balance = 0; public double deposit(double credit) { balance += credit; return this.balance; }
}
这被转换为以下JavaScript代码:
var BankAccount = (function () {
function BankAccount() { this.balance = 0; } BankAccount.prototype.deposit = function(credit) { this.balance += credit; return this.balance; }; return BankAccount;
})();
类可以定义构造函数,具有超类,并且可以像在Java中一样实例化。与Java类似,JSweet中允许使用内部类和匿名类(从1.1.0版开始)。JSweet支持静态和常规内部/匿名类,它们可以与封闭类共享状态。仍然像在Java中一样,匿名类可以访问其作用域中声明的最终变量。例如,以下声明在JSweet中有效,并且将在运行时模仿Java语义,以便Java程序员可以受益于Java语言的所有功能。
abstract class C {
public abstract int m();
}
public class ContainerClass {
// inner class public class InnerClass { public I aMethod(final int i) { // anonymous class return new C() { @Override public int m() { // access to final variable i return i; } } } }
}
接口
在JSweet中,可以像在Java中一样使用接口。但是,与Java相反,没有关联的类可用作运行时。使用接口时,JSweet会生成代码来模拟特定的Java行为(例如instanceof在接口上)。
JSweet支持Java 8静态和默认方法。但是,默认方法到目前为止都是实验性的,你应该自担风险使用它们。
在JSweet中,接口更类似于TypeScript中的接口而不是Java中的接口。这意味着它们必须被视为对象签名,它可以指定函数,还可以指定属性。为了在定义接口时允许使用字段作为属性,JSweet允许使用带注释的常规类@jsweet.lang.Interface。例如,以下接口键入Point具有2个属性的对象。
@Interface
public class Point {
public double x; public double y;
}
对于Java程序员来说,这可能看起来像是一种非常奇怪的方法来定义一个对象,但是你必须记住它不是一个类,而是一个JavaScript对象的类型。因此,它不违反OOP原则。我们可以创建一个在界面后键入的JavaScript对象。请注意,以下代码实际上并未创建Point 接口的实例,而是创建符合接口的对象。
Point p1 = new Point() {{ x=1; y=1; }};
此对象创建机制是TypeScript / JavaScript机制,不应与匿名类混淆,匿名类是类似Java的构造。因为Point有注释@Interface,转换后的JavaScript代码类似于:
var p1 = Object.defineProperty({ x:1, y:1 }, "_interfaces", ["Point"]);
请注意,对于每个对象,JSweet会跟踪其创建的接口以及其类实现的所有可能接口。此接口跟踪系统实现为一个名为的特殊对象属性__interfaces。使用该属性,JSweet允许instanceof在Java之类的接口上使用运算符。
接口中的可选字段
接口可以定义可选字段,用于在程序员忘记初始化对象中的必填字段时报告错误。在JSweet中支持可选字段是通过使用 @jsweet.lang.Optional注释完成的。例如:
@Interface
public class Point {
public double x; public double y; @Optional public double z = 0;
}
在从接口构造对象时,JSweet编译器将检查字段是否已正确初始化。
// no errors (z is optional)
Point p1 = new Point() {{ x=1; y=1; }};
// JSweet reports a compile error since y is not optional
Point p2 = new Point() {{ x=1; z=1; }};
接口中的特殊JavaScript函数
在JavaScript中,对象可以具有属性和函数,但也可以(非排他地)用作构造函数和函数本身。这在Java中是不可能的,因此JSweet定义了用于处理这些情况的特殊函数。
$apply 用于表示该对象可以用作函数。
$new 用于声明该对象可以用作构造函数。
例如,如果一个对象o是O定义 的接口$apply(),则写:
o.$apply();
将转变为:
o();
同样,如果O定义$new():
o.$new();
将转变为:
new o();
是的,它在Java中没有意义,但在JavaScript中确实如此!
无类型对象(地图)
在JavaScript中,对象可以看作包含键值对的映射(键通常称为索引,尤其是当它是数字时)。因此,在JSweet中,所有对象都定义了特殊函数(定义于 def.js.Object):
$get(key) 使用给定键访问值。
$set(key,value) 设置或替换给定键的值。
$delete(key) 删除给定键的值。
反射/无类型访问
的功能$get(key),$set(key,value)并且$delete(key)可以被看作是一个简单的反射API来访问对象字段和状态。还要注意静态方法def.js.Object.keys(object),它返回给定对象上定义的所有键。
以下代码使用此API来内省对象的状态 o。
for(String key : def.js.Object.keys(o)) {
console.log("key=" + key + " value=" + o.$get(key));
});
当没有给定对象的类型化API时,此API可用于以无类型方式操作对象(当然应尽可能避免使用)。
无类型对象初始化
可以使用该$set(key,value)函数创建新的无类型对象。例如:
Object point = new def.js.Object() {{ $set("x", 1); $set("y", 1); }};
它也转化为:
var point = { "x": 1, "y": 1};
作为一种快捷方式,可以使用该jsweet.util.Lang.$map函数,该函数转换为完全相同的JavaScript代码:
import static jsweet.util.Lang.$map;
[...]
Object point = $map("x", 1, "y", 1);
索引对象
可以为每个对象重载键和值的类型。例如,Array
对于使用数字键索引的对象,允许实现java.lang.Iterable接口,以便可以在foreach循环中使用它们。例如,NodeList类型(来自DOM)定义了一个索引函数:
@Interface
class NodeList implements java.lang.Iterable {
public double length; public Node item(double index); public Node $get(double index);
}
在JSweet中,您可以使用该$get 函数访问节点列表元素,也可以使用foreach语法进行迭代。以下代码生成完全有效的JavaScript代码。
NodeList nodes = ...
for (int i = 0; i < nodes.length; i++) {
HTMLElement element = (HTMLElement) nodes.$get(i); [...]
}
// same as:
NodeList nodes = ...
for (Node node : nodes) {
HTMLElement element = (HTMLElement) node; [...]
}
枚举
JSweet允许类似于Java定义枚举。下面的代码声明与树可能值的枚举(A,B,和C)。
enum MyEnum {
A, B, C
}
以下语句是JSweet中的有效语句。
MyEnum e = MyEnum.A;
assert MyEnum.A == e;
assert e.name() == "A";
assert e.ordinal() == 0;
assert MyEnum.valueOf("A") == e;
assert array(MyEnum.values()).indexOf(MyEnum.valueOf("C")) == 2;
与Java枚举一样,可以在枚举中添加其他方法,构造函数和字段。
enum ScreenRatio {
FREE_RATIO(null), RATIO_4_3(4f / 3), RATIO_3_2(1.5f), RATIO_16_9(16f / 9), RATIO_2_1(2f / 1f), SQUARE_RATIO(1f); private final Float value; private MyComplexEnum(Float value) { this.value = value; } public Float getValue() { return value; }
}
枚举便携性说明
简单的枚举被转换为常规的TypeScript枚举,即数字。在JavaScript中,在运行时,枚举实例是简单编码的序数。因此,JSweet枚举很容易与TypeScript枚举共享,即使使用枚举,JSweet程序也可以与TypeScript程序进行互操作。
具有其他成员的枚举也会映射到TypeScript枚举,但会生成另一个类来存储其他信息。与TypeScript互操作时,序号将保留,但附加信息将丢失。想要与TypeScript共享枚举的程序员应该知道这种行为。
全局
在Java中,与JavaScript相反,没有全局变量或函数(只有静态成员,但即使那些必须属于一个类)。因此,JSweet引入了保留的Globals 类和globals包。这有两个目的:
生成具有全局变量和函数的代码(在Java中不鼓励这样做)
绑定到定义全局变量和函数的现有JavaScript代码(尽可能多的JavaScript框架)
在Globals类中,只允许使用静态字段(全局变量)和静态方法(全局函数)。以下是适用于Globals类的主要约束:
没有非静态成员
没有超级课程
不能延长
不能用作常规类的类型
没有公共构造函数(空私有构造函数可以)
不能在方法中使用$ get,$ set和$ delete
例如,以下代码片段将引发转换错误。
class Globals {
public int a; // error: public constructors are not allowed public Globals() { this.a = 3; } public static void test() { // error: no instance is available $delete("key"); }
}
// error: Globals classes cannot be used as types
Globals myVariable = null;
必须记住,Globals类和global包在运行时被擦除,以便可以直接访问它们的成员。例如mypackage.Globals.m(),在JSweet程序中,对应mypackage.m()于生成的代码中的函数以及运行时的JavaScript VM中的 函数。此外,mypackage.globals.Globals.m()对应于m()。
为了擦除生成代码中的包,程序员也可以使用@Root注释,这将在第5节中解释 。
可选参数和重载
In JavaScript, parameters can be optional, in the sense that a parameter value does not need to be provided when calling a function. Except for varargs, which are fully supported in JSweet, the general concept of an optional parameter does not exist in Java. To simulate optional parameters, JSweet programmers can use method overloading, which is supported in Java. Here are some examples of supported overloads in JSweet:
String m(String s, double n) { return s + n; }
// simple overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
// complex overloading (JSweet generates more complex code to mimic the Java behavior)
String m(String s) { return s; }
Bridging to external JavaScript elements
It can be the case that programmers need to use existing libraries from JSweet. In most cases, one should look up in the available candies, a.k.a. bridges at http://www.jsweet.org/jsweet-... When the candy does not exist, or does not entirely cover what is needed, one can create new definitions in the program just by placing them in the def.libname package. Definitions only specify the types of external libraries, but no implementations. Definitions are similar to TypeScript’s .d.ts definition files (actually JSweet generates intermediate TypeScript definition files for compilation purposes). Definitions can also be seen as similar to .h C/C++ header files.
Examples
以下示例显示了使用简单定义可供JSweet程序员访问的主干存储类。此类仅用于键入,将作为TypeScript定义生成,并在JavaScript生成期间擦除。
package def.backbone;
class Store {
public Store(String dbName) {}
}
请注意,定义类构造函数必须具有空体。此外,定义类方法必须是native。例如:
package def.mylib;
class MyExternalJavaScriptClass {
public native myExternalJavaScriptMethod();
}
可以在定义中定义属性,但是,无法初始化这些属性。
写定义规则(又名桥梁)
按照惯例,将类放在def.libname包中定义了一组libname名为的外部JavaScript库 的定义libname。请注意,此机制类似于TypeScript d.ts 定义文件。
Candies(外部JavaScript库的桥梁)使用定义。例如,jQuery candy定义了def.jquery包中的所有jQuery API 。
以下是编写定义时需要遵循的规则和约束的列表。
接口比类更受欢迎,因为接口可以合并,类可以实例化。仅当API定义显式构造函数(可以使用其创建对象new)时,才应使用类。要在JSweet中定义接口,只需使用注释类@jsweet.lang.Interface。
必须将顶级函数和变量定义为类中的public static 成员Globals。
所有类,接口和包都应记录在Javadoc标准之后的注释。
当函数参数有多种类型时,方法重载应优先于使用union类型。当无法进行方法重载时,简单地使用Object类型会更方便。键入的强度较低,但更容易使用。
可以使用字符串类型来提供函数重载,具体取决于字符串参数值。
在方法签名中,可以使用@jsweet.lang.Optional注释定义可选参数 。
在界面中,可以使用@jsweet.lang.Optional注释定义可选字段 。
定义可以直接嵌入到JSweet项目中,以便以类型化的方式访问外部库。
定义也可以打包成糖果(Maven工件),以便它们可以被其他项目共享。有关如何创建糖果的完整详细信息,请参阅“ 包装”部分。请注意,在使用JSweet编写库时不需要编写定义,因为Java API可以直接访问,并且可以使用该declaration选项由JSweet自动生成TypeScript定义。
无法访问
有时,定义不可用或不正确,只需要一个小补丁即可访问功能。程序员必须记住,JSweet只是一个语法层,并且总是可以绕过键入以访问未在API中明确指定的字段或函数。
虽然具有良好类型的API是首选和建议的方式,但是当这样的API不可用时,使用def.js.Object.$get允许反射访问方法和属性,然后可以将其转换为正确的类型。为了以无类型方式访问函数,可以强制转换 def.js.Function并调用泛型和非类型化方法$apply。例如,以下是$在jQuery API不可用时如何调用jQuery 方法:
import def.dom.Globals.window;
[...]
Function $ = (Function)window.$get("$");
$.$apply("aCssSelector"):
该$get函数可用于def.js.Object(或子类)的实例。对于a def.js.Object,您可以使用jsweet.util.Lang.object辅助方法强制转换它 。例如:
import static jsweet.dom.Lang.object;
[...]
object(anyObject).$get("$");
最后,jsweet.util.Lang.$inserthelper方法允许用户在程序中插入任何TypeScript表达式。无效的表达式将引发TypeScript编译错误,但不建议使用此技术。
import static jsweet.dom.Lang.$get;
import static jsweet.dom.Lang.$apply;
[...]
// generate anyObject"prop";
$apply($get(anyObject, "prop"), "param");
最后,还要注意使用jsweet.util.Lang.any辅助方法,这对于擦除键入非常有用。由于该any方法any在TypeScript中生成类型的强制转换,因此它比例如强制转换更激进Object。以下示例说明如何使用该any方法将Int32ArrayJava转换为Java int[](然后允许对其进行直接索引访问)。
ArrayBuffer arb = new ArrayBuffer(2 2 4);
int[] array = any(new Int32Array(arb));
int whatever = array[0];
混入
在JavaScript中,通常的做法是使用新闻元素(字段和方法)来增强现有类。它是框架定义插件时使用的扩展机制。通常,jQuery插件会向JQuery类中添加新元素。例如,jQuery计时器插件timer向JQuery该类添加一个字段。因此,JQuery如果您多带带使用jQuery,或者使用其计时器插件增强jQuery ,则 该类没有相同的原型。
在Java中,此扩展机制存在问题,因为Java语言默认情况下不支持mixins或任何类型的扩展。
无法访问mixins
程序员可以使用访问$get器和/或强力转换来访问添加的元素。
以下是$get用于计时器插件的示例:
((Timer)$("#myId").$get("timer")).pause();
这是另一种通过使用jQuery UI插件来实现它的方法(请注意,此解决方案强制使用def.jqueryui.JQuery 而不是def.jquery.JQuery为了访问menu()由UI插件添加的功能):
import def.jqueryui.JQuery;
[...]
Object obj = $("#myMenu");
JQuery jq = (JQuery) obj;
jq.menu();
然而,这些解决方案并不完全令人满意,因为在打字方面明显不安全。
使用mixins键入访问
当需要交叉糖果动态扩展时,JSweet定义了mixin的概念。mixin是一个定义成员的类,最终可以在目标类(mixin-ed类)中直接访问。Mixins使用@Mixin注释定义。这是def.jqueryui.JQuerymixin 的摘录 :
package def.jqueryui;
import def.dom.MouseEvent;
import def.js.Function;
import def.js.Date;
import def.js.Array;
import def.js.RegExp;
import def.dom.Element;
import def.jquery.JQueryEventObject;
@jsweet.lang.Interface
@jsweet.lang.Mixin(target=def.jquery.JQuery.class)
public abstract class JQuery extends def.jquery.JQuery {
native public JQuery accordion(); native public void accordion(jsweet.util.StringTypes.destroy methodName); native public void accordion(jsweet.util.StringTypes.disable methodName); native public void accordion(jsweet.util.StringTypes.enable methodName); native public void accordion(jsweet.util.StringTypes.refresh methodName); ... native public def.jqueryui.JQuery menu(); ...
人们可以注意到@jsweet.lang.Mixin(target=def.jquery.JQuery.class) ,这个mixin将被合并到一起,def.jquery.JQuery以便用户能够直接以一种良好的方式使用所有UI插件成员。
如何使用
TBD。
从现有的TypeScript定义生成JSweet糖果
TBD。
辅助类型
JSweet使用大多数Java输入功能(包括功能类型),但也使用所谓的辅助类型扩展Java类型系统。辅助类型背后的想法是创建可以通过使用类型参数(也称为泛型)来保存键入信息的类或接口 ,以便JSweet转换器可以涵盖更多的键入方案。这些类型已经从TypeScript类型系统映射,它比Java更丰富(主要是因为JavaScript是一种动态语言,需要比Java更多的打字场景)。
功能类型
用于功能类型,JSweet重用java.Runnable和 java.util.function爪哇8的功能接口,这些接口是通用的,但只支持高达2参数的功能。因此,JSweet为更多参数添加了一些支持jsweet.util.function,因为它是JavaScript API中的常见情况。
以下是使用Function通用功能类型的示例:
import java.util.function.Function;
public class C {
String test(Functionf) { f.apply("a"); } public static void main(String[] args) { String s = new C().test(p -> p); assert s == "a"; }
}
我们鼓励程序员使用jsweet.util.function和java.util.function(以及 java.lang.Runnable)中定义的通用功能接口。当需要具有更多参数的函数时,程序员可以jsweet.util.function通过遵循与现有函数相同的模板来定义他们自己的通用函数类型 。
在某些情况下,程序员更愿意定义自己的特定功能接口。这得到了JSweet的支持。例如:
@FunctionalInterface
interface MyFunction {
void run(int i, String s);
}
public class C {
void m(MyFunction f) { f.run(1, "test"); } public static void main(String[] args) { new C().m((i, s) -> { // do something with i and s }); }
}
重要警告:这里要注意的是,与Java相反,@FunctionInterface注释的使用是强制性的。
还要注意apply函数的可能用途,按照惯例,该函数始终是目标对象的功能定义(除非使用apply注释进行@Name注释)。定义/调用 apply可以在任何类/对象上完成(因为在JavaScript中任何对象都可以成为一个功能对象)。
对象类型
对象类型与接口类似:它们定义了一组适用于对象的字段和方法(但请记住它是一个编译时合同)。在TypeScript中,对象类型是内联的和匿名的。例如,在TypeScript中,以下方法m接受一个参数,该参数是包含index字段的对象:
// TypeScript:
public class C {
public m(param : { index : number }) { ... }
}
对象类型是编写较短代码的便捷方式。可以通过动态构建对象来传递正确键入的对象:
// TypeScript:
var c : C = ...;
c.m({ index : 2 });
显然,对象类型是一种使程序员很容易输入JavaScript程序的方法,这是TypeScript的主要目标之一。它使得JavaScript程序员的输入简洁,直观,直观。在Java / JSweet中,不存在类似的内联类型,Java程序员用于为这种情况定义类或接口。因此,在JSweet中,程序员必须定义用@ObjectType对象类型注释的辅助类。这可能看起来更复杂,但它有利于强制程序员命名所有类型,最终可以根据上下文导致更易读和可维护的代码。请注意,与接口类似,对象类型在运行时被擦除。另外@ObjectType 注解的类可以内部类,使他们在本地使用。
这是以前的TypeScript程序的JSweet版本。
public class C {
@ObjectType public static class Indexed { int index; } public void m(Indexed param) { ... }
}
使用对象类型与使用接口类似:
C c = ...;
c.m(new Indexed() {{ index = 2; }});
当对象类型是共享对象并表示可以在多个上下文中使用的键入实体时,建议使用 @Interface注释而不是@ObjectType。这是基于界面的版本。
@Interface
public class Indexed {
int index;
}
public class C {
public m(Indexed param) { ... }
}
C c = ...;
c.m(new Indexed {{ index = 2; }});
字符串类型
在TypeScript中,字符串类型是一种根据字符串参数的值来模拟函数重载的方法。例如,这是DOM TypeScript定义文件的简化摘录:
// TypeScript:
interface Document {
[...] getElementsByTagName(tagname: "a"): NodeListOf; getElementsByTagName(tagname: "b"): NodeListOf ; getElementsByTagName(tagname: "body"): NodeListOf ; getElementsByTagName(tagname: "button"): NodeListOf ; [...]
}
在此代码中,getElementsByTagName函数都是依赖于传递给tagname参数的字符串的重载。不仅字符串类型允许函数重载(通常在TypeScript / JavaScript中不允许),但它们也约束字符串值(类似于枚举),因此编译器可以自动检测字符串值中的拼写错误并引发错误。
此功能对代码质量很有用,JSweet提供了一种机制来模拟具有相同级别类型安全性的字符串类型。字符串类型是使用注释的公共静态字段@StringType。必须使用在同一容器类型中声明的同名接口键入它。
对于JSweet翻译库(糖果),所有字符串类型都在类中声明jsweet.util.StringTypes,因此程序员很容易找到它们。举例来说,如果一个"body"字符串类型需要定义,一个名为Java接口body和一个静态的最终场被称为body在一个定义jsweet.util.StringTypes。
请注意,每个糖果可能在jsweet.util.StringTypes类中定义了自己的字符串类型 。JSweet转换器在字节码级别合并所有这些类,以便所有糖果的所有字符串类型在同一个jsweet.util.StringTypes实用程序类中可用。因此,JSweet DOM API将如下所示:
@Interface
public class Document {
[...] public native NodeListOfgetElementsByTagName(a tagname); public native NodeListOf getElementsByTagName(b tagname); public native NodeListOf getElementsByTagName(body tagname); public native NodeListOf getElementsByTagName(button tagname); [...]
}
在此API中a,b,body和button是在定义的接口 jsweet.util.StringTypes类。当使用一种方法时 Document,程序员只需要使用相应的类型实例(同名)。例如:
Document doc = ...;
NodeListOf
注意:如果字符串值不是有效的Java标识符(例如 "2d"或者"string-with-dashes"),则将其转换为有效的标识符并使用注释@Name("originalName"),以便JSweet转换器知道必须在生成的代码中使用什么实际字符串值。例如,默认情况下,"2d"和"string-with-dashes"将对应于接口StringTypes._2d和 StringTypes.string_with_dashes与@Name注解。
程序员可以根据自己的需要定义字符串类型,如下所示:
import jsweet.lang.Erased;
import jsweet.lang.StringType;
public class CustomStringTypes {
@Erased public interface abc {} @StringType public static final abc abc = null; // This method takes a string type parameter void m2(abc arg) { } public static void main(String[] args) { new CustomStringTypes().m2(abc); }
}
注意使用@Erased注释,它允许声明abc内部接口。此接口用于键入字符串类型字段abc。通常,我们建议程序员将程序的所有字符串类型分组到同一个实用程序类中,以便于查找它们。
元组类型
元组类型表示具有多带带跟踪的元素类型的JavaScript数组。对于的元组类型,JSweet定义参数化辅助类TupleN
例如,给定以下大小为2的元组:
Tuple2
我们可以期待以下(良好类型)行为:
assert tuple.$0 == "test";
assert tuple.$1 == 10;
tuple.$0 = "ok";
tuple.$1--;
assert tuple.$0 == "ok";
assert tuple.$1 == 9;
元组类型都在jsweet.util.tuple包中定义(并且必须定义) 。默认情况下Tuple[2..6],定义了类。当在糖果API中遇到时,会自动生成其他元组(> 6)。当然,当需要在jsweet.util.tuple包中找不到更大的元组时,程序员可以根据需要在该包中添加自己的元组,只需遵循与现有元组相同的模板即可。
联盟类型
联合类型表示可能具有多个不同表示之一的值。当这种情况发生在方法签名中时(例如,允许给定参数的几种类型的方法),JSweet利用了Java中可用的方法重载机制。例如,以下m方法接受参数p,该参数可以是a String或a Integer。
public void m(String p) {...}
public void m(Integer p) {...}
在前一种情况下,不需要使用显式联合类型。对于更一般的情况,JSweet 在 包中定义了一个辅助接口 Union
以下代码显示了JSweet中union类型的典型用法。它只是将一个变量声明为一个字符串和一个数字之间的联合,这意味着该变量实际上可以是其中一种类型(但没有其他类型)。从联合类型到常规类型的切换是通过jsweet.util.Lang.union辅助方法完成的。这个辅助方法是完全无类型的,允许从Java角度将任何联合转换为另一种类型。它实际上是JSweet转换器,它检查是否一直使用了union类型。
import static jsweet.util.Lang.union;
import jsweet.util.union.Union;
[...]
Union
// u can be used as a String
String s = union(u);
// or a number
Number n = union(u);
// but nothing else
Date d = union(u); // JSweet error
如果union需要,也可以使用其他方式将助手从常规类型切换回联合类型。
import static jsweet.util.Lang.union;
import jsweet.util.union.Union3;
[...]
public void m(Union3
[...]
// u can be a String, a Number or a Date
m(union("a string"));
// but nothing else
m(union(new RegExp(".*"))); // compile error
注意:在键入函数参数时,优先使用Java函数重载而不是union类型。例如:
// with union types (discouraged)
native public void m(Union3
// with overloading (preferred way)
native public void m(String s);
native public void m(Number n);
native public void m(Date d);
交叉类型
TypeScript定义了类型交集的概念。当类型相交时,意味着结果类型是更大的类型,它是所有相交类型的总和。例如,在TypeScript中, A & B对应于定义两者A和B成员的类型。
由于许多原因,Java中的交集类型无法轻松实现。因此,这里做出的实际选择是使用联合类型代替交集类型。A & B因此,在JSweet中定义为 Union,这意味着程序员可以使用辅助方法访问这两者A和 B成员jsweet.util.Lang.union。它当然不如TypeScript版本方便,但它仍然是类型安全的。
语义
语义指定给定程序在执行时的行为方式。虽然JSweet依赖于Java语法,但程序被转换为JavaScript并且不在JRE中运行。因此,与Java程序相比,JavaScript语义将影响JSweet程序的最终语义。在本节中,我们将通过关注Java / JavaSript和JSweet之间的差异或共性来讨论语义。
主要方法
主要方法是程序执行入口点,并且在main评估包含方法的类时将全局调用。例如:
public class C {
private int n; public static C instance; public static void main(String[] args) { instance = new C(); instance.n = 4; } public int getN() { return n; }
}
// when the source file containing C has been evaluated:
assert C.instance != null;
assert C.instance.getN() == 4;
全局调用main方法的方式取决于程序的打包方式。有关详细信息,请参阅附录。
初始化器
初始化器的行为与Java类似。
例如:
public class C1 {
int n; { n = 4; }
}
assert new C1().n == 4;
与静态初始化器类似:
public class C2 {
static int n; static { n = 4; }
}
assert C2.n == 4;
虽然在实例化类时会评估常规初始值设定项,但是为了避免前向依赖性问题而懒惰地评估静态初始化程序,并模拟初始化程序的Java行为。使用JSweet,程序员可以定义静态字段或静态初始化程序,它依赖于尚未初始化的静态字段。
有关此行为的更多详细信息,请参阅附录。
数组初始化和分配
数组可以像Java一样使用。
String[] strings = { "a", "b", "c" };
assert strings[1] == "b";
指定维度时,数组是预先分配的(如Java中所示),因此它们使用正确的长度进行初始化,并在多维数组的情况下使用正确的子数组进行初始化。
String[][] strings = new String2;
assert strings.length == 2;
assert strings[0].length == 2;
strings0 = "a";
assert strings0 == "a";
通过强制转换为def.js.Arraywith,可以在数组上使用JavaScript API jsweet.util.Lang.array。
import static jsweet.util.Lang.array;
[...]
String[] strings = { "a", "b", "c" };
assert strings.length == 3;
array(strings).push("d");
assert strings.length == 4;
assert strings[3] == "d";
在某些情况下,最好def.js.Array直接使用该类。
Array
// same as: Array
// same as: Array
assert strings.length == 3;
strings.push("d");
assert strings.length == 4;
assert strings.$get(3) == "d";
异步编程
在ES2015 + Promise API的帮助下,JSweet支持基本回调概念之外的高级异步编程。
承诺
通过声明Promise返回类型来定义异步方法非常简单 。当毫秒毫秒过去时,Promise将 满足以下方法。
Promise
return new Promise
setTimeout(resolve, millis);
});
}
然后,您可以在履行承诺后链接同步和异步操作。
delay(1000)
// chain with a synchronous action with "then". Here we just return a constant.
.then(() -> {
System.out.println("wait complete"); return 42;
})
// chain with an asynchronous action with "thenAsync". Here it is implied that anotherAsyncAction(String) returns a Promise<...>
.thenAsync((Integer result) -> {
System.out.println("previous task result: " + result); // will print "previous task result: 42" return anotherAsyncAction("param");
})
// this chained action will be executed once anotherAsyncAction finishes its execution.
.then((String result) -> {
System.out.println("anotherAsyncAction returned " + result);
})
// catch errors during process using this method
.Catch(error -> {
System.out.println("error is " + error);
});
这允许完全类型安全和流畅的异步编程模型。
异步/ AWAIT
Promises非常有趣,以避免回调,但编写它仍然需要大量的样板代码。它比纯回调更好,但比线性编程更不易读和直接。这就是async/await帮助的地方 。
使用await关键字,您可以告诉运行时等待 Promise实现,而无需编写then方法。await“是” then部分之后的代码。结果是您可以使用线性编程编写异步代码。
import static jsweet.util.Lang.await;
// wait for the Promise returned by the delay method to be fulfilled
await(delay(1000));
System.out.println("wait complete");
错误处理也是如此。您可以使用普通的 try / catch习语来处理异常。
import static jsweet.util.Lang.await;
import def.js.Error;
try {
Integer promiseResult = await(getANumber());
assert promiseResult == 42;
} catch(Error e) {
System.err.println("something unexpected happened: " + e);
}
你必须声明为async每个异步方法/ lambda(即每个等待某事的方法)。
import static jsweet.util.Lang.await;
import static jsweet.util.Lang.async;
import static jsweet.util.Lang.function;
import jsweet.lang.Async;
import def.js.Function;
@Async
Promise
await(delay(1000)); // won"t compile if the enclosing method isn"t @Async
return asyncReturn(42); // converts to Promise
}
@Async
void askAnswerThenVerifyAndPrintIt() {
try {
Integer answer = await(findAnswer()); // lambda expressions can be async Function verifyAnswerAsync = async(function(() -> { return await(answerService.verifyAnswer(answer)); })) Boolean verified = await(verifyAnswerAsync.$apply()); if (!verified) { throw new Error("cannot verify this answer"); } console.log("answer found: " + answer);
} catch (Error e) {
console.error(e, "asynchronous process failed");
}
}
甜,不是吗?;)
姓名冲突
与TypeScript / JavaScript相反,Java在方法,字段和包之间存在根本区别。Java还支持方法重载(具有相同名称的不同签名的方法)。在JavaScript中,对象变量和函数存储在同一个对象映射中,这基本上意味着您不能为多个对象成员使用相同的密钥(这也解释了Java中无法实现Java语义中的方法重载)。因此,在TypeScript中生成时,某些Java代码可能包含名称冲突。JSweet将尽可能自动避免名称冲突,并在其他情况下报告声音错误。
方法和字段名称冲突
JSweet执行转换以自动允许方法和私有字段具有相同的名称。另一方面,同一个类或具有子类链接的类中不允许使用相同名称的方法和公共字段。
为了避免由于这种JavaScript行为导致编程错误,JSweet添加了一个语义检查来检测类中的重复名称(这也考虑了在父类中定义的成员)。举个例子:
public class NameClashes {
// error: field name clashes with existing method name public String a; // error: method name clashes with existing field name public void a() { return a; }
}
方法重载
与TypeScript和JavaScript相反(但与Java类似),JSweet中可能有多个具有相同名称但具有不同参数的方法(所谓的重载)。我们区分了简单的重载和复杂的重载。简单重载是使用方法重载来定义可选参数。JSweet允许这个习惯用语,它对应于以下模板:
String m(String s, double n) { return s + n; }
// valid overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
在这种情况下,JSweet将生成JavaScript代码,只有一个方法具有可选参数的默认值,因此生成的程序的行为对应于原始程序。在这种情况下:
function m(s, n = 0) { return s + n; }
如果程序员尝试以不同方式使用重载,例如通过为同一方法名定义两个不同的实现,JSweet将回退复杂的重载,包括生成根实现(包含更多参数的方法)和一个辅助实现per overloading方法(用表示方法签名的后缀命名)。根实现是通用的,并通过测试给定参数的值和类型调度到其他实现。例如:
String m(String s, double n) { return s + n; }
String m(String s) { return s; }
生成以下(略微简化的)JavaScript代码:
function m(s, n) {
if(typeof s === "string" && typeof n === "number") { return s + n; } else if(typeof s === "string" && n === undefined) { return this.m$java_lang_String(s); } else { throw new Error("invalid overload"); }
}
function m$java_lang_String(s) { return s; }
局部变量名称
在TypeScript / JavaScript中,局部变量可能与使用全局方法冲突。例如,使用alertDOM(jsweet.dom.Globals.alert)中的全局方法要求没有局部变量隐藏它:
import static jsweet.dom.Globals.alert;
[...]
public void m1(boolean alert) {
// JSweet compile error: name clash between parameter and method call alert("test");
}
public void m2() {
// JSweet compile error: name clash between local variable and method call String alert = "test"; alert(alert);
}
请注意,在调用全局方法时使用完全限定名称时也会发生此问题(这是因为限定条件在TypeScript / JavaScript中被删除)。在任何情况下,JSweet都会在发生此类问题时报告声音错误,以便程序员可以调整局部变量名称以避免与全局变量发生冲突。
测试对象的类型
要在运行时测试给定对象的类型,可以使用instanceofJava运算符,也可以使用 Object.getClass()函数。
instanceof
这instanceof是在运行时测试类型的建议和首选方法。JSweet将transpile到常规instanceof或一个 typeof取决于所测试的类型的操作(这将在回退 typeof对number,string和boolean核心类型)。
尽管不是必需的,但也可以使用实用方法直接使用typeof JSweet中的运算符jsweet.util.Lang.typeof。以下是有效类型测试的一些示例:
import static jsweet.util.Lang.typeof;
import static jsweet.util.Lang.equalsStrict;
[...]
Number n1 = 2;
Object n2 = 2;
int n3 = 2;
Object s = "test";
MyClass c = new MyClass();
assert n1 instanceof Number; // transpiles to a typeof
assert n2 instanceof Number; // transpiles to a typeof
assert n2 instanceof Integer; // transpiles to a typeof
assert !(n2 instanceof String); // transpiles to a typeof
assert s instanceof String; // transpiles to a typeof
assert !(s instanceof Integer); // transpiles to a typeof
assert c instanceof MyClass;
assert typeof(n3) == "number";
从JSweet版本1.1.0开始,instanceof接口上也允许运算符,因为JSweet会跟踪所有对象的所有实现接口。通过调用的对象中的附加隐藏属性来确保此接口跟踪,__interfaces并且包含对象实现的所有接口的名称(直接或通过在编译时确定的类继承树)。因此,如果instanceof 运算符的类型参数是接口,JSweet只是检查对象的 __interfaces字段是否存在并包含给定的接口。例如,当Point接口是这个代码在JSweet中完全有效:
Point p1 = new Point() {{ x=1; y=1; }};
[...]
assert p1 instanceof Point
Object.getClass() 和 X.class
在JSweet中,可以使用Object.getClass()on any实例。它实际上将返回类的构造函数。X.class如果X是类,using 也将返回构造函数。因此,以下断言将在JSweet中保留:
String s = "abc";
assert String.class == s.getClass()
在课堂上,您可以调用getSimpleName()或getName()函数。
String s = "abc";
assert "String" == s.getClass().getSimpleName()
assert String.class.getSimpleName() == s.getClass().getSimpleName()
请注意,getSimpleName()或者getName()函数也适用于接口。但是,您必须知道,X.class如果X是接口,将以字符串形式编码(保存接口的名称)。
限制和约束
由于所有数字都映射到JavaScript数字,因此JSweet不区分整数和浮点数。因此, 无论实际类型是什么n instanceof Integer,n instanceof Float都会给出相同的结果n。对于字符串和字符存在相同的限制,这些字符串和字符在运行时无法区分,但也适用于具有相同参数数量的函数。例如,一个实例IntFunction
这些限制对函数重载有直接影响,因为重载使用instanceof运算符来决定调用哪个重载。
就像在JavaScript中工作时一样,序列化对象必须与其实际类正确“复活”,以便 instanceof操作员可以再次工作。例如,通过创建的点对象Point p = (Point)JSON.parse("{x:1,y:1}")不会对instanceof运算符起作用。如果您遇到这样的用例,您可以联系我们获取一些有用的JSweet代码以正确恢复对象类型。
lambda表达式中的变量作用域
已知JavaScript变量作用域给程序员带来了一些问题,因为可以从使用此变量的lambda外部更改对变量的引用。因此,JavaScript程序员不能依赖于在lambda范围之外声明的变量,因为当执行lambda时,该变量可能已在程序中的其他位置被修改。例如,以下程序显示了一个典型案例:
NodeList nodes = document.querySelectorAll(".control");
for (int i = 0; i < nodes.length; i++) {
HTMLElement element = (HTMLElement) nodes.$get(i); // final element.addEventListener("keyup", (evt) -> { // this element variable will not change here element.classList.add("hit"); });
}
在JavaScript中(注意EcmaScript 6修复了这个问题),这样的程序将无法实现其目的,因为element事件监听器中使用的变量被for循环修改并且不保持期望值。在JSweet中,这些问题与最终的Java变量类似。在我们的示例中,element变量在lambda表达式中重新定义,以便封闭循环不会更改其值,因此程序的行为与Java类似(正如大多数程序员所预期的那样)。
这个范围
与JavaScript相反,与Java类似,将方法用作lambda将防止丢失对引用的引用this。例如,在action以下程序的方法中this,即使在方法中action被称为lambda 时,也保持正确的值main。虽然这对Java程序员来说似乎是合乎逻辑的,但JavaScript语义并不能确保这种行为。
package example;
import static jsweet.dom.Globals.console;
public class Example {
private int i = 8; public Runnable getAction() { return this::action; } public void action() { console.log(this.i); // this.i is 8 } public static void main(String[] args) { Example instance = new Example(); instance.getAction().run(); }
}
重要的是要强调this通过与ES5 bind功能类似的机制确保正确的值。结果是函数引用被包装在函数中,这意味着函数指针(例如this::action)动态地创建包装函数。它在操作函数指针时有副作用,这个问题在本期https://github.com/cincheo/js... 。
打包
打包是JavaScript的复杂点之一,尤其是来自Java时。JavaScript包装的复杂性归结为JavaScript本身没有定义任何包装这一事实。因此,多年来出现了许多事实上的解决方案和指南,使得对常规Java程序员的包装理解不安。JSweet提供了有用的选项并生成代码,以便通过使包装问题更加透明和大多数情况下的Java“简单”来简化Java程序员的生活。在本节中,我们将描述和解释典型的包装方案。
使用您的文件,无需任何包装
运行程序最常见和最简单的情况就是将每个生成的文件包含在HTML页面中。这不是任何包装选项的默认模式。例如,当您的程序定义两个类x.y.z.A并x.y.z.B在两个多带带的文件中时,您可以按如下方式使用它们:
[...]
这样做时,程序员需要非常谨慎,以避免文件之间的正向静态依赖关系。换句话说,A 类不能B在静态字段,静态初始化程序或静态导入中使用任何内容,否则在尝试加载页面时会导致运行时错误。此外,A该类不能扩展B该类。这些约束来自JavaScript / TypeScript,与JSweet无关。
可以想象,使用这种手动技术运行简单的程序很好,但是对于开发复杂的应用程序会变得非常不舒服。复杂的应用程序大多数时候使用适当的工具捆绑和/或打包程序,以避免必须手动处理JavaScript文件之间的依赖关系。
为浏览器创建捆绑包
为了避免必须手动处理依赖项,程序员使用捆绑工具将其类捆绑到一个文件中。这样的包使用以下内容包含在任何网页中:
[...]
JSweet带有这样的捆绑设施。要创建一个包文件,只需设定true的bundleJSweet的选项。请注意,您还可以设置true的declaration,询问JSweet生成打字稿定义文件(可选bundle.d.ts)。此文件允许您以类型合适的方式使用/编译来自TypeScript的JSweet程序。
JSweet捆绑选项的“神奇之处”在于它分析源代码中的依赖关系,并在构建捆绑时负责解决前向引用。特别是,JSweet为静态字段和初始化器实现了一个惰性初始化机制,以便分解跨类的静态前向引用。程序员没有具体的附加声明使其工作(与TypeScript相反)。
请注意,它仍然存在一些小的限制(例如,在使用内部和匿名类时),但很少遇到这些限制,并且将在以后的版本中删除。
另请注意,如果您随module 选项一起指定选项,JSweet将引发错误bundle。
包装模块
首先,让我们从解释模块开始,重点关注Java 包(或TypeScript 命名空间)和模块之间的区别。如果您对差异感到满意,请跳过此部分。
包和模块是两个相似的概念,但适用于不同的上下文。必须将Java包理解为编译时命名空间。它们允许通过名称路径对程序进行编译时结构化,具有隐式或显式可见性规则。软件包通常对程序实际捆绑和部署的方式影响不大。
必须将模块理解为部署/运行时“捆绑”,这可以required由其他模块实现。与Java世界中最接近模块的概念可能是OSGi包。模块定义导入和导出的元素,以便它们创建强大的运行时结构,可用于独立部署软件组件,从而避免名称冲突。例如,对于模块,两个不同的库可以定义一个util.List类,并且实际上在同一个VM上运行和使用,没有命名问题(只要库被捆绑在不同的模块中)。
如今,许多库都是通过模块打包和访问的。在浏览器中使用模块的标准方法是AMD,但在Node.js中它是commonjs模块系统。
JSweet中的模块
JSweet支持用于打包的AMD,commonjs和UMD模块系统。JSweet定义了一个module选项(值:amd,commonjs或umd)。指定此选项时,JSweet会根据简单规则自动创建默认模块组织:一个文件=一个模块。
例如,当将module选项设置为打包时commonjs,可以编写:
node target/js/x/y/z/MyMainClass.js
哪里MyMainClass包含main方法。
模块系统将自动处理参考,并在需要时需要其他模块。在引擎盖下,JSweet分析了Java import语句并将它们转换为require指令。
注意:一旦使用该module选项编译程序,就可以使用适当的工具(如Browserify)将其打包为捆绑包,这样可以提供与使用bundleJSweet选项类似的输出。还要注意,同时指定时JSweet会引发错误module和bundle,这是排斥的选项。
外部模块
使用module选项编译JSweet程序时,必须将所有外部库和组件作为外部模块。只需使用@Module(name) 注释,JSweet就可以自动需要模块。在JSweet中,导入或使用带有注释的类或成员@Module(name)将在运行时自动需要相应的模块。请注意,只有使用该module选项生成代码时才会出现这种情况。如果该module选项处于禁用状态,@Module 则会忽略注释。
package def.jquery;
public final class Globals extends def.js.Object {
... @jsweet.lang.Module("jquery") native public static def.jquery.JQuery $(java.lang.String selector); ...
}
上面的代码显示了JSweet jQuery API的摘录。我们可以注意到,该$函数带有注释@Module("jquery")。因此,对此函数的任何调用都将触发jquery模块的要求 。
注意:未来版本中可能会提供模块的手动要求概念。但是,自动需求对于大多数程序员来说已经足够,并且隐藏了必须明确要求模块的复杂性。无论是否使用模块,它还具有使用相同代码的优点。
故障排除:当糖果没有正确定义@Module 注释时,可以在一个名为的特殊文件的注释中强制声明module_defs.java。例如,要强制将BABYLONBabylonjs candy 的 名称空间导出为 babylonjs模块,可以编写以下文件:
package myprogram;
// declare module "babylonjs" {
// export = BABYLON;
// }
请注意,JSweet项目只能定义一个module_defs.java文件,该文件应包含注释中的所有模块声明。另请注意,这是一个黑客攻击,首选方法是为糖果做出贡献来解决问题。
根包
Root包是一种调整生成的代码的方法,以便在生成的代码中擦除JSweet包,从而在运行时删除。要设置根包,只需定义一个package-info.java文件并使用@Root 包上的注释,如下所示:
@Root
package a.b.c;
上述声明意味着该c包是一个根包,即它将在生成的代码及其所有父包中被删除。因此,如果c包含一个包d和一个类C,它们将是运行时的顶级对象。换句话说,a.b.c.d成为 d,a.b.c.C变成C。
请注意,由于在封装之前放置的@Root封装被擦除,因此在封装之前不能定义任何类型@Root。在前面的示例中,a和b包必须是空包。
不使用模块时的行为(默认)
默认情况下,root包不会更改生成的文件的文件夹层次结构。例如,a.b.c.C该类仍将在
不使用模块时(默认),可以有多个@Root 包(但@Root包不能包含另一个@Root 包)。
使用模块时的行为
使用模块时(请参阅模块选项),只@Root允许一个@Root包,当有一个包时,其他包或类型不能超出该@Root包的范围。然后,生成的文件夹/文件层次结构从根包开始,以便实际擦除之前的所有文件夹。
包装JSweet jar(糖果)
糖果是一种Maven工件,包含从JSweet客户端程序轻松访问JavaScript库所需的所有内容。该库可以是外部JavaScript库,TypeScript程序或其他JSweet程序。
糖果的解剖学
与
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75287.html
摘要:例如允许的对象默认情况下,通过使用内置宏将核心对象和方法映射到。例如这被转换为以下代码类可以定义构造函数,具有超类,并且可以像在中一样实例化。因此,它不违反原则。用于声明该对象可以用作构造函数。 这个工具可以将java代码转为js代码,从而可以使用java编写前端代码 如果排版看着费劲可以下载下方html,打开html后使用google翻译 JSweet语言规范版本:2.x(快照) 作...
摘要:语言行为及特征状态看不懂任何英语技术,英语文档,凡事没有培训部在搞的,只有英文文档的东西国内一律没大公司在用,都非主流,排斥英文文档和新技术,以及各种超出他学习能力范围的技术。 在撰写此文前首先必须申明的是本人不鄙视任何一种框架,也无意于挑起PHP框架间的战争,更没有贬低某个框架使用者的用意,本文纯粹个人的看法。你可以认为我无知也好,或者装逼也好,请不要试着在任何情况下,随便发起言语的...
摘要:每次都是自己手动切换下中文显示。需求分析点击英文的搜索结果自动跳转到中文文档地址。其实我要做的也很简单,就是在页面加载的时候,使表单选择中文简体然后重新提交一次表单即可。解决方案解决方案就是使用代码选择中文简体并且提交表单。 MDN社区(即Mozilla开发者社区)具有很多高质量中英文文档。它是我开发时遇到概念模糊的地方经常访问的网站。因为默认搜索一些代码,优先显示的都是英文。但是恰恰...
摘要:而程序员和医生律师的不同点在于持续学习上。两个小问题是需要收费,一年大概刀图书都是英文的。的视频基本都有英文字幕,配合作者的,英语不好的同学学习也没有问题。英文好的有技术功底的同学多发表一些观点,其他的同学都 摘要: 行业发展得太快,你必须学习,纯靠经验积累行不通,技术淘汰的速度远大于你经验积累的速度。 非鸡汤:不要和程序员谈自己的编程历史,很多的经验在今天已经不适用了。只要2-3年...
摘要:代码代码戳这里插件预备知识首先给出一本参考的中文书籍,在练习的过程中有帮到忙。你还可以重写别的页面,比如书签管理页面等,可以参考文档中文翻译过来应该叫内容脚本,它可以运行在你指定的页面之中,可以拿到指定页面的一些信息。 前言 这是一篇关于Chrome扩展插件入门、Vue.js入门的小练习,功能是:在当前浏览的页面点击扩展图标,并点击保存之后,该页面就会存在你的新标签页中。其实就是一个可...
阅读 840·2021-11-15 17:58
阅读 3640·2021-11-12 10:36
阅读 3778·2021-09-22 16:06
阅读 955·2021-09-10 10:50
阅读 1324·2019-08-30 11:19
阅读 3308·2019-08-29 16:26
阅读 927·2019-08-29 10:55
阅读 3340·2019-08-26 13:48