我有几个类不是从任何超类派生的。他们都定义了大量相同的方法。例如,
class A {
def getMsgNum = 1
}
class B {
def getMsgNum = 2
}
我想编写一个泛型函数,它将根据调用的对象函数返回消息num。等等,
def getMsgNum[T](t: T) = t.getMsgNum
我认为,由于类型擦除,我不能指望它可以工作,但我正在查看与ClassTag绑定的视图绑定和上下文,但仍然无效。
def getType[T: ClassTag](msg: T) = {
msg.getMsgNum
}
我来自C ++背景,我正在尝试为每种类型实现模板编译的效果。
谢谢你的时间!
答案 0 :(得分:5)
我个人更喜欢adhoc多态与TypeClass(http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html)模式。我认为对于这类问题,它将是更加“真正的scala方式”解决方案。结构化输入在运行时也更昂贵,因为它使用反射进行字段访问。
class A
class B
trait ToMsgNum[T] {
def getMsgNum: Int
}
implicit object AToMsgNum extends ToMsgNum[A] {
def getMsgNum = 1
}
implicit object BToMsgNum extends ToMsgNum[B] {
def getMsgNum = 2
}
def getMsgNum[T: ToMsgNum](t: T) =
implicitly[ToMsgNum[T]].getMsgNum
println(getMsgNum(new A))
println(getMsgNum(new B))
答案 1 :(得分:4)
def getMsgNum[T](t: T)(implicit ev: T => { def getMsgNum: Int }) = t.getMsgNum
其中{ def getMsgNum: Int }
是结构类型。来自documentation:
结构类型是Parent {Decls}形式的一种类型,其中Decls包含不覆盖Parents中任何成员的新成员的声明。
和
结构类型提供了极大的灵活性,因为它们无需先验地定义继承层次结构
请注意,上述解决方案使用隐式反射调用来访问结构类型的字段,这是一种必须通过添加导入显式启用的语言功能
import scala.language.reflectiveCalls
答案 2 :(得分:3)
这与Eugene的解决方案没有什么不同,但我认为它更清晰一点:
// predefined classes you have no access to
class Foo { def someMethod = "foo" }
class Bar { def someMethod = "bar" }
除了反射或结构类型(伪装反射)之外,Scala中没有办法在这些类型上一般调用someMethod
。 可以工作的方式是通过定义知道如何单独处理每种类型的适配器对象,然后对它们进行泛型调用:
trait HasSomeMethod[T] { def someMethod(x: T): String }
object FooHasSomeMethod extends HasSomeMethod[Foo] { def someMethod(x: Foo) = x.someMethod }
object BarHasSomeMethod extends HasSomeMethod[Bar] { def someMethod(x: Bar) = x.someMethod }
现在您可以将其中一个适配器对象传递到需要对Foo#someMethod
和Bar#someMethod
进行通用访问的方法中:
def invokeSomeMethod[T](x: T)(adapter: HasSomeMethod[T]) =
adapter.someMethod(x)
invokeSomeMethod(new Foo)(FooHasSomeMethod) // returns "foo"
invokeSomeMethod(new Bar)(BarHasSomeMethod) // returns "bar"
(我们可以在这里使用单个参数列表,但稍后我们将会列出2个列表)
然而,这显然没有我们想要的那么有用,因为我们必须手动传入适配器。让我们介绍一下implicits,让Scala自动查找正确的适配器对象并将其传递给我们的通用但无继承的方法:
implicit object FooHasSomeMethod extends HasSomeMethod[Foo] { ... }
implicit object BarHasSomeMethod extends HasSomeMethod[Bar] { ... }
def invokeSomeMethod[T](x: T)(implicit adapter: HasSomeMethod[T]) =
adapter.someMethod(x)
现在这些工作:
invokeSomeMethod(new Foo) // returns "foo"
invokeSomeMethod(new Bar) // returns "bar"
以上2个电话会自动转换为之前版本中较长的电话; Scala会自动从隐式对象(以及implicit adapter
和val
)中查找def
参数的合适值,这在调用的“环境”中是可用的。 / p>
您也可以像这样定义invokeSomeMethod
,这只是上述定义的语法糖:
def invokeSomeMethod[T: HasSomeMethod](x: T) =
implicitly[HasSomeMethod[T]].someMethod(x)
或者,由于T: HasSomeMethod
自动生成第二个参数列表implicit evidence$1: HasSomeMethod[T]
,这也有效:
def invokeSomeMethod[T: HasSomeMethod](x: T) =
evidence$1.someMethod(x)
上述“模式”称为Type Classes。因此,例如,T: HasSomeMethod
位可以读作“属于类型类T
的某些类型HasSomeMethod
”(或“......已成为类型类的实例{ {1}}“)。
有关类型类的详细信息,请参阅例如http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
您还可以为甚至没有HasSomeMethod
的类定义HasSomeMethod
类型类实例,也不会与someMethod
和Foo
有任何其他相似之处,如果需要的话:
Bar
如果您需要为许多类定义该类型类的实例,您可以拥有一个帮助程序(名称与类型类匹配,以获得良好性):
implicit object IntHasSomeMethod extends HasSomeMethod[Int] {
def someMethod(x: Int) = "this is an int: " + x
}
invokeSomeMethod(3) // returns "this is an int: 3"
现在您可以非常简洁地定义类型类实例(适配器):
def HasSomeMethod[T](fn: T => String) = new HasSomeMethod[T] {
def someMethod(x: T) = fn(x)
}
答案 3 :(得分:0)
如果你不想使用结构类型(反射)和隐式,那么如何在它上面创建适配器,所以你自己的方法getMsgNum将基于适配器而不是已经存在的类来实现。
class A {
def getMsgNum = 1
}
class B {
def getMsgNum = 2
}
class C {
def getMsgNum = 3
}
trait Adaptor[T] {
def getMsgNum: Int
}
class AdaptorA(t: A) extends Adaptor[A] {
def getMsgNum = t.getMsgNum
}
class AdaptorB(t: B) extends Adaptor[B] {
def getMsgNum = t.getMsgNum
}
class AdaptorC(t: C) extends Adaptor[C] {
def getMsgNum = t.getMsgNum
}
def getMsgNum[T](t: Adaptor[T]) = t.getMsgNum
getMsgNum(new AdaptorA(new A)) //1
getMsgNum(new AdaptorB(new B)) //2
getMsgNum(new AdaptorC(new C)) //3