高级概念
Android 数据编组
NativeScript 无缝处理 JavaScript 和 Java/Kotlin 之间的数据类型转换,利用类型推断和专用包装器,确保跨平台开发中的平滑集成和类型安全。
字符串转换
将 JavaScript 字符串转换为 Java 字符串类型
JavaScript String 映射到 java.lang.String
var context = ...;
var button = new android.widget.Button(context);
var text = "My Button";
button.setText(text);
button.setText(text)
- text
转换为 java.lang.String
将 JavaScript 字符串转换为 Kotlin 字符串类型
JavaScript String 映射到 kotlin.String
const kotlinClass = new com.example.KotlinClassWithStringProperty()
var text = 'My Button'
kotlinClass.setStringProperty(text) /
kotlinClass.setStringProperty(text)
- JavaScript text
转换为 kotlin.String
将 Java 字符串类型转换为 JavaScript 字符串
java.lang.String 和 java.lang.Character 类型都投影为 JavaScript String
var file = new java.io.File('/path/to/file')
var path = file.getPath()
getPath()
- 返回 java.lang.String
,转换为 JS String
将 Kotlin 字符串类型转换为 JavaScript 字符串
kotlin.String 和 kotlin.Char 类型都投影为 JavaScript String
package com.example
class KotlinClassWithStringAndCharProperty {
val stringProperty: String = "string property"
val charProperty: Char = 'c'
}
var kotlinClass = new com.example.KotlinClassWithStringAndCharProperty()
var str1 = kotlinClass.getStringProperty() // returns kotlin.String, converted to JS String
var str2 = kotlinClass.getCharProperty() // returns kotlin.Char, converted to JS String
getStringProperty()
- 返回kotlin.String
,转换为JS String
getCharProperty()
- 返回kotlin.Char
,转换为 JSString
布尔值转换
JavaScript 布尔值到 Java 布尔值类型
JavaScript Boolean 映射到 Java 基本布尔值。
var context = ...;
var button = new android.widget.Button(context);
var enabled = false;
button.setEnabled(enabled);
button.setEnabled(enabled)
- JavaScript 布尔值 enabled
转换为 Java 基本布尔值。
JavaScript 布尔值到 Kotlin 布尔值类型
JavaScript Boolean 映射到 Kotlin 类 Boolean。
从 Java 布尔值转换为 JavaScript 布尔值
基本 boolean 和引用 java.lang.Boolean 类型都投影为 JavaScript Boolean
var context = ...
var button = new android.widget.Button(context);
var enabled = button.isEnabled();
isEnabled()
- 返回 基本布尔值
,转换为 JS Boolean
从 Kotlin 布尔值转换为 JavaScript 布尔值
Kotlin 的布尔值类型 kotlin.Boolean 映射到 JavaScript Boolean
package com.example
class KotlinClassWithBooleanProperty {
val booleanProperty: Boolean = false
}
var kotlinClass = new com.example.KotlinClassWithBooleanProperty()
var enabled = kotlinClass.getBooleanProperty() // returns Kotlin Boolean, converted to JS Boolean
getBooleanProperty()
- 返回 Kotlin Boolean
,转换为 JS Boolean
。
数值数据类型
将 JavaScript Number 转换为 Java/Kotlin 数值类型
Java 和 Kotlin 有几种基本数值类型,而 JavaScript 只有 Number
类型。此外,与 JavaScript 不同,Java 和 Kotlin 支持 方法重载,这使得数值转换更加复杂。
考虑以下示例
class MyObject extends java.lang.Object {
public void myMethod(byte value){
}
public void myMethod(short value){
}
public void myMethod(int value){
}
public void myMethod(long value){
}
public void myMethod(float value){
}
public void myMethod(double value){
}
}
class MyObject : Any() {
fun myMethod(value: Byte) {}
fun myMethod(value: Short) {}
fun myMethod(value: Int) {}
fun myMethod(value: Long) {}
fun myMethod(value: Float) {}
fun myMethod(value: Double) {}
}
从 JavaScript 调用 myObject
实例上的 myMethod
时,应用以下逻辑
var myObject = new MyObject()
隐式转换
- 整数转换
当你调用
myObject.myMethod(10)
运行时会将 JavaScript 10(Number)
隐式转换为 Java/Kotlin Int
,然后调用 myMethod(Int)
方法。
注意
如果没有 myMethod(Int) 实现,Android 运行时将尝试选择转换损失最小的最佳重载。如果找不到这样的方法,将引发异常。
- 浮点数转换
myObject.myMethod(10.5) // myMethod(Double) will be called.
JavaScript 10.5
Number
将转换为 Java/Kotlin double
,然后调用 myMethod(ouble)。
注意
在不存在 myMethod(double) 实现的情况下,运行时将尝试选择转换损失最小的最合适重载。如果找不到这样的方法,将抛出异常。
显式转换
要显式地将 JavaScript Number
转换为 Java/Kotlin 数值数据类型,并调用特定的方法重载,NativeScript 在全局范围内提供以下函数
byte(number)
→ (Java 基本 byte
|Kotlin Byte
)
- 数值将被截断,只使用其整数部分的第一个字节。short(number)
→ Java 基本 short
将 Java 数值类型转换为 JavaScript Number
以下 Java 类型将转换为 JavaScript Number
- 基本 byte 和引用 java.lang.Byte
var byte = new java.lang.Byte('1')
var jsByteValue = byte.byteValue() // returns primitive byte, converted to Number
- 基本 short 和引用 java.lang.Short
var short = new java.lang.Short('1')
var jsShortValue = short.shortValue() // returns primitive short, converted to Number
- 基本 int 和引用 java.lang.Integer
var int = new java.lang.Integer('1')
var jsIntValue = int.intValue() // returns primitive int, converted to Number
intValue()
- 返回 基本 int
,转换为 Number
- 基本 float 和引用 java.lang.Float
var float = new java.lang.Float('1.5')
var jsFloatValue = float.floatValue() // returns primitive float, converted to Number
floatValue()
返回 基本 float
,转换为 Number
。
- 基本 double 和引用 java.lang.Double
var double = new java.lang.Double('1.5')
var jsDoubleValue = double.doubleValue() // returns primitive double, converted to Number
doubleValue()
返回 基本 float
,转换为 Number
。
- Long & 基本 long
java.lang.Long 及其基本等价物是特殊类型,通过应用以下规则投影到 JavaScript
- If the value is in the interval `(-2^53, 2^53)` then it is converted to [Number](http://www.w3schools.com/jsref/jsref_obj_number.asp)
- Else a special object with the following characteristics is created:
- Has Number.NaN set as a prototype
- Has value property set to the string representation of the Java long value
- Its `valueOf()` method returns NaN
- Its `toString()` method returns the string representation of the Java long value
public class TestClass {
public long getLongNumber54Bits(){
return 1 << 54;
}
public long getLongNumber53Bits(){
return 1 << 53;
}
}
var testClass = new TestClass()
var jsNumber = testClass.getLongNumber53Bits()
var specialObject = testClass.getLongNumber54Bits()
jsNumber
是 JavaScript Number,specialObject
是上面讨论的特殊对象。
将 Kotlin 数值类型转换为 JavaScript Number
与从 Java 数值数据类型到 JavaScript Number 的转换类似,以下 Kotlin 数值数据类型将转换为 JavaScript Number 类型
- Kotlin 的 byte 类型 kotlin.Byte
package com.example
class KotlinClassWithByteProperty {
val byteProperty: Byte = 42
}
var kotlinClass = new com.example.KotlinClassWithByteProperty()
var jsByteValue = kotlinClass.getByteProperty() // returns Kotlin Byte, converted to Number
- Kotlin 的 short 类型 kotlin.Short
package com.example
class KotlinClassWithShortProperty {
val shortProperty: Short = 42
}
var kotlinClass = new com.example.KotlinClassWithShortProperty()
var jsShortValue = kotlinClass.getShortProperty()
getShortProperty()
- 返回 Kotlin Short
,转换为 Number
- Kotlin 的整数类型 kotlin.Int
package com.example
class KotlinClassWithIntProperty {
val intProperty: Int = 42
}
var kotlinClass = new com.example.KotlinClassWithIntProperty()
var jsIntValue = kotlinClass.getIntProperty()
getIntProperty()
- 返回 Kotlin Int
,转换为 Number
- Kotlin 的 float 类型 kotlin.Float
package com.example
class KotlinClassWithFloatProperty {
val floatProperty: Float = 42.0f
}
var kotlinClass = new com.example.KotlinClassWithFloatProperty()
getFloatProperty()
- 返回 Kotlin Float
,转换为 Number
- Kotlin 的 double 类型 kotlin.Double
package com.example
class KotlinClassWithDoubleProperty {
val doubleProperty: Double = 42.0
}
var kotlinClass = new com.example.KotlinClassWithDoubleProperty()
var jsDoubleValue = kotlinClass.getDoubleProperty()
getDoubleProperty()
- 返回 Kotlin double
,转换为 Number
Kotlin 的 long 类型 kotlin.Long 是一种特殊类型,通过应用以下规则投影到 JavaScript
- 如果值在区间
(-2^53, 2^53)
内,则将其转换为 Number - 否则,会创建一个具有以下特征的特殊对象
- 将 Number.NaN 设置为原型
- 将 value 属性设置为 Kotlin 长整型值的字符串表示形式
- 其 valueOf() 方法返回 NaN
- 其 toString() 方法返回 Kotlin 长整型值的字符串表示形式
- 如果值在区间
package com.example
class KotlinClassWithLongProperties {
val longNumber54Bits: Long
get() = (1 shl 54).toLong()
val longNumber53Bits: Long
get() = (1 shl 53).toLong()
}
var kotlinClass = new com.example.KotlinClassWithLongProperties()
var jsNumber = kotlinClass.getLongNumber53Bits()
var specialObject = kotlinClass.getLongNumber54Bits()
jsNumber
是 JavaScript Number,specialObject
是上面讨论的特殊对象。
转换 Undefined & Null
JavaScript Undefined & Null 映射到 Java 和 Kotlin null 字面量(或空指针)。
var context = ...;
var button = new android.widget.Button(context);
button.setOnClickListener(undefined);
在上面的示例中,Java 调用将使用 null
关键字进行。
数组转换
JavaScript 数组 会隐式转换为 Java 数组 或 Kotlin 数组,使用上面描述的数组元素类型转换规则。例如
var items = ['One', 'Two', 'Three']
var myObject = new MyObject()
myObject.myMethod(items)
class MyObject extends java.lang.Object {
public void myMethod(java.lang.String[] items){
}
}
class MyObject : Any() {
fun myMethod(items: Array<String>) {}
}
从 Java/Kotlin 数组转换为 JavaScript 数组
Java/Kotlin 中的数组是一个特殊的 java.lang.Object,它有一个隐式的关联类。Java/Kotlin 数组在 JavaScript 中被投影为一个具有以下特征的特殊 JavaScript 代理对象
- 具有 length 属性
- 已注册索引 getter 和 setter 回调,它们
- 如果数组包含可转换为 JavaScript 类型的元素,则访问第 i 个元素将返回转换后的类型
- 如果数组包含不可转换为 JavaScript 类型的元素,则访问第 i 个元素将返回 Java/Kotlin 类型的代理对象,请参阅 访问 API
var directory = new java.io.File('path/to/myDir')
var files = directory.listFiles() // files is a special object as described above
var singleFile = files[0] // the indexed getter callback is triggered and a proxy object over the java.io.File is returned
package com.example
class KotlinClassWithStringArrayProperty {
val stringArrayProperty: Array<String> = arrayOf("element1", "element2", "element3")
}
var kotlinClass = new com.example.KotlinClassWithStringArrayProperty()
var kotlinArray = kotlinClass.getStringArrayProperty() // kotlinArray is a special object as described above
var firstStringElement = kotlinArray[0] // the indexed getter callback is triggered and the kotlin.String is returned as a JS string
注意
出于性能考虑,Java/Kotlin 数组有意不转换为 JavaScript 数组,尤其是在处理大型数组时。
对象数组
在必须从 JavaScript 创建 Java/Kotlin 数组的场景中。在给定的场景中,我们通过添加一个名为 create 的自定义方法扩展了内置的 JavaScript 数组对象。通过这样做,我们用自己的实现增强了 Array 对象的默认功能,以满足内置 JavaScript Array
对象 中的特定需求。以下是一些关于如何使用 Array.create
方法的示例
// the following statement is equivalent to byte[] byteArr = new byte[10];
var byteArr = Array.create('byte', 10)
// the following statement is equivalent to String[] stringArr = new String[10];
var stringArr = Array.create(java.lang.String, 10)
以下是 Array.create
方法的完整规范
Array.create(elementClassName, length)
Array.create(javaClassCtorFunction, length)
第一个签名接受 elementClassName
的 string
类型。当需要创建 Java 基本类型数组(例如 char
、boolean
、byte
、short
、int
、long
、float
和 double
)时,此选项将变得很有用。当需要创建 Java 不规则数组时,这也适用。对于这种情况,elementClassName
必须是标准的 JNI 类表示法。以下是一些示例
// equivalent to int[][] jaggedIntArray2 = new int[10][];
var jaggedIntArray2 = Array.create('[I', 10)
// equivalent to boolean[][][] jaggedBooleanArray3 = new boolean[10][][];
var jaggedBooleanArray3 = Array.create('[[Z', 10)
// equivalent to Object[][][][] jaggedObjectArray4 = new Object[10][][][];
var jaggedObjectArray4 = Array.create('[[[Ljava.lang.Object;', 10)
第二个签名要求您提供 javaClassCtorFunction
,它必须是表示所需 Java 类型的 JavaScript 构造函数。以下是一些示例
// equivalent to String[] stringArr = new String[10];
var stringArr = Array.create(java.lang.String, 10)
// equivalent to Object[] objectArr = new Object[10];
var objectArr = Array.create(java.lang.Object, 10)
基本类型数组
在处理基本类型数组时,不支持自动编组。要将它们作为参数传递给方法,您需要使用包装类(例如 Integer、Double)将基本类型转换为对象,从而允许自动编组。
public static void myMethod(int[] someParam)
然后你需要按如下方式调用它
let arr = Array.create('int', 3)
arr[0] = 1
arr[1] = 2
arr[2] = 3
SomeObject.myMethod(arr) // assuming the method is accepting an array of primitive types
但是,我们还可以使用一些其他有用的类来创建一些其他基本类型数组
const byteArray = java.nio.ByteBuffer.wrap([1]).array()
const shortArray = java.nio.ShortBuffer.wrap([1]).array()
const intArray = java.nio.IntBuffer.wrap([1]).array()
const longArray = java.nio.LongBuffer.wrap([1]).array()
const floatArray = java.nio.FloatBuffer.wrap([1]).array()
const doubleArray = java.nio.DoubleBuffer.wrap([1]).array()
二维基本类型数组
上述场景在二维数组中变得更加复杂。考虑一个 Java 方法,它接受一个二维数组作为参数
public static void myMethod(java.lang.Integer[][] someParam)
编组后的 JavaScript 代码将如下所示
let arr = Array.create('[Ljava.lang.Integer;', 2)
let elements = Array.create('java.lang.Integer', 3)
elements[0] = new java.lang.Integer(1)
elements[1] = new java.lang.Integer(2)
elements[2] = new java.lang.Integer(3)
arr[0] = elements
SomeObject.myMethod(arr) // assuming the method is accepting a two-dimensional array of primitive types
interface Printer {
fun print(content: String)
fun print(content: String, offset: Int)
}
interface Copier {
fun copy(content: String): String
}
interface Writer {
fun write(arr: Array<Any>)
fun writeLine(arr: Array<Any>)
}
实现接口
public class MyVersatileCopywriter implements Printer, Copier, Writer {
public void print(String content) { ... }
public void print(String content, int offset) { ... }
public String copy(String content) { ... }
public void write(Object[] arr) { ... }
public void writeLine(Object[] arr) { ... }
}
class MyVersatileCopywriter: Printer, Copier, Writer{
override fun print(content: String) { ... }
override fun print(content: String, offset: Int) { ... }
override fun copy(content: String): String { ... }
override fun write(arr: Array<Any>) { ... }
override fun writeLine(arr: Array<Any>) { ... }
}
在 NativeScript 中可以通过扩展继承 Java Object 的任何有效对象来实现相同的结果。
- 在 JavaScript 中 - 在实现中声明一个
interfaces
数组 - 使用 Typescript 语法 - 将
decorator
应用于扩展类(注意@Interfaces([...])
)
使用 Javascript 语法 - 将 interfaces
数组附加到 extend 调用的实现对象
let MyVersatileCopyWriter = java.lang.Object.extend({
interfaces: [com.a.b.Printer, com.a.b.Copier, com.a.b.Writer], /* the interfaces that will be inherited by the resulting class */
print: function() { ... }, /* implementing the 'print' methods from Printer */
copy: function() { ... }, /* implementing the 'copy' method from Copier */
write: function() { ... }, /* implementing the 'write' method from Writer */
writeLine: function() { ... }, /* implementing the 'writeLine' method from Writer */
toString: function() { ... } /* override `java.lang.Object's` `toString */
});
@Interfaces([com.a.b.Printer, com.a.b.Copier, com.a.b.Writer]) /* the interfaces that will be inherited by the resulting MyVersatileCopyWriter class */
class MyVersatileCopyWriter extends java.lang.Object {
constructor() {
super();
return global.__native(this);
}
print() { ... }
copy() { ... }
write() { ... }
writeLine() { ... }
}
警告
实现具有相同方法签名的两个接口将只生成 1 个方法。实现者有责任定义该方法对这两个接口的行为。
实现具有相同方法名称、参数数量但不同返回类型(
void a()
与boolean a()
)的两个接口将导致编译错误。
注意
Java/Kotlin 方法重载由开发人员通过显式检查调用的函数的参数数量来处理。
class MyVersatileCopyWriter extends ... {
constructor() {
super();
return global.__native(this);
}
...
print() {
let content = "";
let offset = 0;
if (arguments.length == 2) {
offset = arguments[1];
}
content = arguments[0];
// do stuff
}
...
}
注意
在 OOP 中,当一个类扩展另一个类(即继承)时,新类不仅可以访问它必须实现的接口方法,还可以覆盖扩展类中的方法。此外,它可以引入特定于新类功能的新方法。这使新类能够扩展和增强其父类的行为,同时提供其自身的其他功能。
NativeScript 中的 Java 嵌套类型
public class Outer {
public class Inner {
// inner and nested class
}
public static class Nested {
// nested but not inner class
}
}
//Instantiate nested types
Outer outer = new Outer();
Outer.Inner inner1 = outer.new Inner();
Outer.Inner inner2 = new Outer().new Inner();
Outer.Nested nested = new Outer.Nested()
var outer = new Outer()
var inner1 = new outer.Inner()
var inner2 = new new Outer().Inner()
var nested = new Outer.Nested()
Kotlin 类型
所有 Kotlin 类型都使用包和类代理投影到 JavaScript,如中所述
Kotlin 伴生对象
您可以通过 Companion
字段访问 Kotlin 的 伴生对象
package com.example
class KotlinClassWithCompanion {
companion object {
fun getDataFromCompanion() = "some data"
}
}
var companion = com.example.KotlinClassWithCompanion.Companion
var data = companion.getDataFromCompanion()
Kotlin 对象
要访问 Kotlin 的 对象,请使用 INSTANCE
字段
package com.example
object KotlinObject {
fun getDataFromObject() = "some data"
}
var objectInstance = com.example.KotlinObject.INSTANCE
var data = objectInstance.getDataFromObject()
访问 Kotlin 属性
要访问 Kotlin 的 属性,请使用其编译器生成的 get/set 方法。非布尔 Kotlin 属性也可以在 NativeScript 应用程序中用作 JS 字段。
package com.example
class KotlinClassWithStringProperty(var stringProperty: kotlin.String)
var kotlinClass = new com.example.KotlinClassWithStringProperty()
var propertyValue = kotlinClass.getStringPropert()
kotlinClass.setStringProperty('example')
propertyValue = kotlinClass.stringProperty
kotlinClass.stringProperty = 'second example'
访问 Kotlin 包级函数
为了使用 Kotlin 包级函数,需要知道其定义所在的类。让我们看一个例子
package com.example
fun getRandomNumber() = 42
var randomNumber = com.example.FunctionsKt.getRandomNumber()
在上面的示例中,类 FunctionsKt
是由 Kotlin 编译器自动生成的,其名称基于定义函数的文件的名称。Kotlin 支持注释文件以使用用户提供的名称,这简化了使用包级函数的过程
var randomNumber = com.example.UtilityFunctions.getRandomNumber()
@file:JvmName("UtilityFunctions")
package com.example
fun getRandomNumber() = 42
访问 Kotlin 扩展函数
为了使用扩展函数,需要知道其定义所在的类。此外,在调用此函数时,第一个参数应该是为其定义函数的类型的实例。让我们看一个例子
package com.example
import java.util.ArrayList
fun ArrayList<String>.switchPlaces(firstElementIndex: Int, secondElementIndex: Int) {
val temp = this[firstElementIndex]
this[firstElementIndex] = this[secondElementIndex]
this[secondElementIndex] = temp
}
var arrayList = new java.util.ArrayList()
arrayList.add('firstElement')
arrayList.add('secondElement')
com.example.Extensions.switchPlaces(arrayList, 0, 1)
在上面的示例中,类 ExtensionsKt
是由 Kotlin 编译器自动生成的,其名称基于定义函数的文件的名称。Kotlin 支持注释文件以使用用户提供的名称,这简化了使用包级函数的过程
@file:JvmName("ExtensionFunctions")
package com.example
import java.util.ArrayList
fun ArrayList<String>.switchPlaces(firstElementIndex: Int, secondElementIndex: Int) {
val temp = this[firstElementIndex]
this[firstElementIndex] = this[secondElementIndex]
this[secondElementIndex] = temp
}
var arrayList = new java.util.ArrayList()
arrayList.add('firstElement')
arrayList.add('secondElement')
com.example.ExtensionFunctions.switchPlaces(arrayList, 0, 1)
- 上一页
- iOS 运行时类型