英文:
Can Scala class implement Java interface with automatic type conversion
问题
这个Java接口在一个库中被定义:
public interface Interface<T> {
List<T> makeList();
}
我想要创建一个Scala类来实现它,但返回一个Scala列表而不是java.util.List
。以下是我能想到的最好方法,但它似乎不起作用:
import scala.jdk.CollectionConverters._
class Implementation[T] extends Interface[T] {
implicit def s2j[V](list: List[V]): java.util.List[V] = list.asJava // 这似乎没有起作用
override def makeList(): List[T] = ???
}
错误信息是:error overriding method makeList in trait Interface of type (): java.util.List[T]; method makeList of type (): List[T] has incompatible type
。显然,我可以让每个使用Implementation
的用户自行进行转换,但我希望有更简洁的方法。
**编辑:**根据回复,似乎这是不可能的。
英文:
This Java interface is defined in a library:
public interface Interface<T> {
List<T> makeList();
}
I'd like to have a Scala class that implements it, but returns a Scala list instead of java.util.List
. This is the best I could think of but it's not working:
import scala.jdk.CollectionConverters._
class Implementation[T] extends Interface[T] {
implicit def s2j[V](list: List[V]): java.util.List[V] = list.asJava // This doesn't seem to do anything
override def makeList(): List[T] = ???
}
error overriding method makeList in trait Interface of type (): java.util.List[T]; method makeList of type (): List[T] has incompatible type
. Clearly, I can make each user of Implementation
do its own conversion, but I was hoping for something neater.
EDIT: From the responses, it appears that this is not possible.
答案1
得分: 1
你可以创建一个抽象类来构建一个名为 "proxy layer" 的代理层:
import scala.jdk.CollectionConverters._
abstract class ScalaInterface[T] extends Interface[T] {
def makeScalaList(): List[T]
override def makeList(): java.util.List[T] = makeScalaList().asJava
}
class Implementation[T] extends ScalaInterface[T] {
override def makeScalaList(): List[T] = ???
}
不要实现 Interface
,而是实现 ScalaInterface
。
英文:
You can create a "proxy layer" with an abstract class:
import scala.jdk.CollectionConverters._
abstract class ScalaInterface[T] extends Interface[T] {
def makeScalaList(): List[T]
override def makeList(): java.util.List[T] = makeScalaList().asJava
}
class Implementation[T] extends ScalaInterface[T] {
override def makeScalaList(): List[T] = ???
}
Instead of implementing Interface
, you implement ScalaInterface
.
答案2
得分: -1
你不需要隐式转换来做这个。实际上,隐式转换现在通常被视为不好的事情。
只需创建你的 Scala 列表,并在接口需要时显式转换它。
import scala.jdk.CollectionConverters._
class Implementation[T] extends Interface[T] {
def makeScalaList(): List[T]
override def makeList(): java.util.List[T] = makeScalaList().asJava
}
@Igor 问道:
谢谢。我明白我可以这样做。关键是让 Scala 实现对 Scala 用户来说无缝,同时使 Scala 实现在需要 java 类型接口的地方工作。
这不会自动工作。要理解 Java/Scala 互操作性(以及二进制兼容性),重要的是要理解 Scala 是如何将隐式转换或命名参数等语法糖映射到 JVM 字节码的。JVM 设计用于运行 Java,因此不支持这些 Scala 概念。为了解决这个问题,Scala 在调用站点而不是定义站点注入代码。
当你写下面的 Scala 代码时:
implicit def s2j[V](list: List[V]): java.util.List[V] = list.asJava
val list: List[String] = ...
val javaSubList = list.subList(0, 3)
Scala 编译器注意到 subList
不是 scala.collection.List
的成员。然后它寻找转换,将其转换为定义方法的类型,然后在调用站点将两个方法调用链接在一起。Java 代码看起来像这样:
public <V> java.util.List<V> s2j(list: scala.collection.List<V>) {
new scala.jdk.CollectionConverter.SeqHasJava(list).asJava()
}
scala.collection.List<String> list = ...
java.util.List<String> javaSubList = s2j(list).subList(0, 3)
因此,虽然隐式转换感觉像是向类型添加了额外功能,但实际上是每次调用者需要时编译器都会插入代码。由于 Java 和 JVM 不支持这些功能,唯一能够工作的方式是如果 Scala 编译器将代码注入到定义站点。由于它没有这样做,你必须在定义站点自己创建代码,这就是 @Gaël's 回答中的代理/桥接方法所做的事情。如果你仔细观察,你会发现它只是将我的代码的一半放在一个抽象类中,以将互操作代码与 Scala 代码分开。
英文:
You don't need an implicit conversion to do this. In fact implicit conversions are generally regarded as a bad thing now.
Just create your Scala list and convert it explicitly when required by the interface.
import scala.jdk.CollectionConverters._
class Implementation[T] extends Interface[T] {
def makeScalaList(): List[T]
override def makeList(): java.util.List[T] = makeScalaList().asJava
}
@Igor asked:
> Thanks. I understand I can do that. The point is to make it seemless to the users of the Scala implementation, yet make the scala implementation work where the java type Interface is expected.
This will not wont work automatically. To understand Java/Scala interop (and binary compatibility) it is important to understand how Scala maps syntactic sugar like implicit conversions or named parameters to jvm bytecode. The jvm is designed to run Java so lacks these Scala concepts. To get around this Scala injects code at the call site not the definition site.
When you write the following Scala.
implicit def s2j[V](list: List[V]): java.util.List[V] = list.asJava
val list: List[String] = ...
val javaSubList = list.subList(0, 3)
The Scala compiler notices that subList
is not a member of scala.collection.List
. Then it looks for conversions to convert it to a type where the method is defined then chains to two method calls together at the call site. The Java would look like:
public <V> java.util.List<V> s2j(list: scala.collection.List[V]) {
new scala.jdk.CollectionConverter.SeqHasJava(list).asJava()
}
scala.collection.List<String> list = ...
java.util.List<String> javaSubList = s2j(list).subList(0, 3)
So while it feels like implicit conversions are adding extra functionality to a type, really the compiler is inserting code every time the caller requires it. Since Java and the JVM do not support these features, the only way this would work is if the Scala compiler injected the code into the definition site. Since it does not you must create the code in the definition site yourself which is what the proxy/bridge in @Gaël's answer does.
If you look closely you will see that all it does is put half of my code into an abstract class to separate the interop code from the Scala code.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论