scala - Scala 适配器模式 - 自动允许具有相同方法的类的“鸭子类型”
问题描述
假设A
在某些代码中使用了 class ,而我想使用 class B
,它具有与 class 完全相同的方法A
- 没有B
extend A
。解决这个问题的最简单方法是什么?换句话说,我正在寻找简单的即用型和通用实现adaptBtoA
(它应该适用于具有相同结构/方法的任何两个类)。
class A {
def foo(x: String) = "A_" + x
}
class B {
def foo(x: String) = "B_" + x
}
def bar(a: A) = {
// ...
}
bar(adaptBtoA(new B()))
如果您熟悉 Go 的鸭子类型接口,那么这就是我所针对的语义。
编辑
我认为由于类型擦除,通用解决方案可能是不可能的,尽管我不确定。mockito
这是我使用该库的尝试:
def adapt[F, T](impl: F): T = mock[T](new Answer[Any]() {
override def answer(inv: InvocationOnMock): Any =
classOf[T]
.getDeclaredMethod(inv.getMethod.getName, inv.getMethod.getParameterTypes:_*)
.invoke(impl, inv.getArguments:_*)
})
val a: A = adapt[B, A](new B())
val res = a.foo("test") // should be "B_test" but errors in compilation
不幸的是,这不起作用,因为我收到以下编译器错误:
type arguments [T] conform to the bounds of none of the overloaded alternatives of
value mock: [T <: AnyRef](name: String)(implicit classTag: scala.reflect.ClassTag[T])T <and> [T <: AnyRef](mockSettings: org.mockito.MockSettings)(implicit classTag: scala.reflect.ClassTag[T])T <and> [T <: AnyRef](defaultAnswer: org.mockito.stubbing.Answer[_])(implicit classTag: scala.reflect.ClassTag[T])T <and> [T <: AnyRef](implicit classTag: scala.reflect.ClassTag[T])T
但是,我可以为特定用例使用硬编码类型:
def adaptBtoA(b: B): A = mock[A](new Answer[Any]() {
override def answer(inv: InvocationOnMock): Any =
classOf[B]
.getDeclaredMethod(inv.getMethod.getName, inv.getMethod.getParameterTypes:_*)
.invoke(b, inv.getArguments:_*)
})
val a: A = adaptBtoA(new B())
val res = a.foo("test") // res == "B_test"
如果在运行时从模板参数获取类类型信息是不可能的,也许我可以使用宏adapt
在编译时生成我需要的所有函数?然后代码将如下所示:
genAdapt[B, A]()
genAdapt[D, C]()
// etc...
但是我对 scala 宏的了解还不够,无法实现这一点,或者这是否可能。
解决方案
您在尝试该adapt
方法时错过了一些事情。编译器说:它需要T
extendsAnyRef
和 a ClassTag[T]
。您还需要 a ClassTag[F]
,因为您会调用 on 上的方法F
,而不是T
.
def adapt[F: ClassTag, T <: AnyRef : ClassTag](impl: F): T = {
mock[T](new Answer[Any]() {
override def answer(inv: InvocationOnMock): Any =
implicitly[ClassTag[F]].runtimeClass
.getDeclaredMethod(inv.getMethod.getName, inv.getMethod.getParameterTypes: _*)
.invoke(impl, inv.getArguments: _*)
})
}
adapt[B, A](new B()).foo("test") // "B_test"
adapt[A, B](new A()).foo("test") // "A_test"
推荐阅读
- vuejs3 - 执行渲染功能期间未处理的错误
- python - 在 Python 中使用 Tkinter 退出时的消息框对话框
- spring - 为 springdoc/io.swagger.core.v3 "ApiResponse" 指定响应类型
- git - 在 Eclipse Git 中找不到“添加到索引”选项
- java - 如何将 fileStream 转换为 blob 以将其添加到 sql 表中?
- c++ - 在 lambda 中使用仿函数
- python - 在 Pandas 中,如何根据 groupby 标准获取多个子集数据框?
- java - 需要帮助在字符串问题中替换名称(clojure)
- android - Android,layoutParams 是否指向父视图而不是视图本身?
- sftp - 如何使用 Deno 读取/写入 sftp 服务器