我有几类不是从任何超类派生的。他们都定义了一堆相同的方法。例如,

class A {
    def getMsgNum = 1
}

class B {
    def getMsgNum = 2
}


我想编写一个通用函数,该函数将基于调用的对象函数返回消息号。所以像

def getMsgNum[T](t: T) = t.getMsgNum


我认为由于类型擦除,我不能指望它能起作用,但是我正在查看与ClassTag绑定的视图绑定和上下文,但这仍然无法正常工作。

def getType[T: ClassTag](msg: T) = {
    msg.getMsgNum
}


我来自C ++背景,我正在尝试为每种类型的模板编译实现某种效果。

谢谢你的时间!

最佳答案

这与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#someMethodBar#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个列表)

但是,这显然没有我们想要的有用,因为我们必须手动传递适配器。让我们介绍一下隐式函数,使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 adapterval)自动为def参数查找合适的值。



您还可以这样定义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”(或“ ...已成为类型类HasSomeMethod的实例”)。

有关类型类的更多信息,请参见http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html



如果需要,您还可以为什至没有HasSomeMethod也不与someMethodFoo没有其他相似之处的类定义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)
}


现在,您可以非常简洁地定义类型类实例(适配器):

implicit val FooHasSomeMethod = HasSomeMethod[Foo](_.someMethod)
implicit val BarHasSomeMethod = HasSomeMethod[Bar](_.someMethod)
implicit val IntHasSomeMethod = HasSomeMethod[Int]("this is an int: " + _)
implicit val PersonHasSomeMethod = HasSomeMethod[Person](_.name)
// etc

09-10 16:32