Scala.js 平台相关方法实现(或依赖注入)
Scala.js platform dependent method implementations (or dependency injection)
tl;dr:像 scalajs-java-time 这样的库如何替换依赖项?
我 运行 使用 Scala.js 进入这个依赖注入:
在共享代码中:
object Example {
def method(...) = { ... dependsOnPlatform ... }
val dependsOnPlatform = ???
}
最简单的做法是:
val dependsOnPlatform = if(onJS) jsImplementation else jvmImplementaion
问题是经常不能为 JVM 编译 jsImplementation
,因为它依赖于 Scala.js 类;如果使用反射或引用 Java 类.
,则可能无法完成 jvmImplementaion
的 fastOptJS
鉴于 object Example
必须是静态的,不能使用构造函数注入。
用反射很容易解决问题,但是在Scala.js中也是不允许的。
它可以通过副作用来解决,但由于对象是静态的,那将是脆弱的。
依赖项可以是 method
的隐式参数,但这需要额外的导入。
这不是宏可以在不复杂构建过程的情况下解决的问题。
正确的解决方案是创建 Java class 和 "shadowing" 它与 Scala.js class.
在共享代码中:
val dependsOnPlatform = new Injection
class InjectionForJVM { ... } //InjectionForJVM.scala
public class Injection extends InjectionForJVM { } //Injection.java
然后在项目的 Scala.js 部分:
class Injection { ... } //Injection.scala
这两个 Injection
classes 必须在同一个包中并且具有相同的接口。
不要将 InjectionForJVM.scala
包裹在任何对象或 class 中,因为 Injection.java
可能无法正确编译。 InjectionForJVM.scala
可以同时使用反射和 Java 方法,因为 class 将无法被 fastOptJS
访问。也可以直接在 Java 中实现 Injection.java
,但谁可能想要这样做呢?
(准确地说,这不是真正的阴影,因为在任何情况下 Scala.js 都不存在 Injection.java
。JVM 上的 classes 的实际阴影是可能的,但是通常是不可取的。)
推荐的解决方案是使用 top-level 对象,该对象在 js/src/
中的实现方式与 jvm/src/
中的不同,但向两者公开相同的接口。然后可以从 shared/src/
代码中正常使用该对象的 API。此对象通常称为 Platform
并制作为 package-private。
js/src/.../Platform.scala
package foo
private[foo] object Platform {
def dependsOnPlatform(...) = {
jsImplementation
}
}
jvm/src/.../Platform.scala
package foo
private[foo] object Platform {
def dependsOnPlatform(...) = {
jvmImplementation
}
}
shared/src/.../Platform.scala
package foo
object Example {
def method(...) = {
...
Platform.dependsOnPlatform(...)
...
}
}
不需要使用丑陋的 "shadowing" hack 也不需要编写 .java 源文件,如@acdenh 的回答中所述。
tl;dr:像 scalajs-java-time 这样的库如何替换依赖项?
我 运行 使用 Scala.js 进入这个依赖注入:
在共享代码中:
object Example {
def method(...) = { ... dependsOnPlatform ... }
val dependsOnPlatform = ???
}
最简单的做法是:
val dependsOnPlatform = if(onJS) jsImplementation else jvmImplementaion
问题是经常不能为 JVM 编译 jsImplementation
,因为它依赖于 Scala.js 类;如果使用反射或引用 Java 类.
jvmImplementaion
的 fastOptJS
鉴于 object Example
必须是静态的,不能使用构造函数注入。
用反射很容易解决问题,但是在Scala.js中也是不允许的。
它可以通过副作用来解决,但由于对象是静态的,那将是脆弱的。
依赖项可以是 method
的隐式参数,但这需要额外的导入。
这不是宏可以在不复杂构建过程的情况下解决的问题。
正确的解决方案是创建 Java class 和 "shadowing" 它与 Scala.js class.
在共享代码中:
val dependsOnPlatform = new Injection
class InjectionForJVM { ... } //InjectionForJVM.scala
public class Injection extends InjectionForJVM { } //Injection.java
然后在项目的 Scala.js 部分:
class Injection { ... } //Injection.scala
这两个 Injection
classes 必须在同一个包中并且具有相同的接口。
不要将 InjectionForJVM.scala
包裹在任何对象或 class 中,因为 Injection.java
可能无法正确编译。 InjectionForJVM.scala
可以同时使用反射和 Java 方法,因为 class 将无法被 fastOptJS
访问。也可以直接在 Java 中实现 Injection.java
,但谁可能想要这样做呢?
(准确地说,这不是真正的阴影,因为在任何情况下 Scala.js 都不存在 Injection.java
。JVM 上的 classes 的实际阴影是可能的,但是通常是不可取的。)
推荐的解决方案是使用 top-level 对象,该对象在 js/src/
中的实现方式与 jvm/src/
中的不同,但向两者公开相同的接口。然后可以从 shared/src/
代码中正常使用该对象的 API。此对象通常称为 Platform
并制作为 package-private。
js/src/.../Platform.scala
package foo
private[foo] object Platform {
def dependsOnPlatform(...) = {
jsImplementation
}
}
jvm/src/.../Platform.scala
package foo
private[foo] object Platform {
def dependsOnPlatform(...) = {
jvmImplementation
}
}
shared/src/.../Platform.scala
package foo
object Example {
def method(...) = {
...
Platform.dependsOnPlatform(...)
...
}
}
不需要使用丑陋的 "shadowing" hack 也不需要编写 .java 源文件,如@acdenh 的回答中所述。