相同的方法,不同的参数类型,一次性实现

时间:2011-01-09 17:23:18

标签: scala

我有一个(Java)类,其操作如下:

abstract class Holder {
    def set(i: Int): Unit
    def set(s: String): Unit
    def set(b: Boolean): Unit
    ...
}

基本上,all执行相同的任务,但只需采用不同的参数类型。我很想创建一个执行类似这样的通用Accessor [T]:

class Accessor[T](holder: Holder) { 
    def set(value: T) { holder.set(value) }
}

......但是这给了:

<console>:16: error: overloaded method value set with alternatives:
  (s: String)Unit <and>
  (i: Int)Unit
  (b: Boolean)Unit
 cannot be applied to (T)
       def set(value: T) { holder.set(value) }

有什么出路吗?

4 个答案:

答案 0 :(得分:3)

使用反射。

class Setter(obj: AnyRef) {
  val clazz = obj.getClass
  def set[T : Manifest](v: T): Boolean = try {
      val paramType = manifest[T].erasure
      val method = clazz.getMethod("set", paramType)
      method.invoke(obj, v.asInstanceOf[AnyRef])
      true
  } catch {
      case ex => false
  }
}

val holder = ..
val setter = new Setter(holder)
setter.set(5) // returns true
setter.set(1.0) // double not accepted, returns false

在Scala中有一个实验性的快捷方式,但在2.8.0发布之前就被删除了。

答案 1 :(得分:2)

我认为匹配应该很好用

def set(value: T) {
    value match {
         case s: String => holder.set(s)
         case i: Int => holder.set(i)
         case b: Boolean => holder.set(b)
    }
}

答案 2 :(得分:2)

我不完全理解你的用例,但你可能尝试做的一件事 - 如果性能不是最重要的 - 是创建一个包装类,为你转换为通用表单,然后全部您的方法采用该包装类(适当的隐式转换)。例如:

class Wrap(val data: String)
implicit def wrapString(s: String) = new Wrap(s)
implicit def wrapBoolean(b: Boolean) = if (b) new Wrap("T") else new Wrap("F")
implicit def wrapLong(l: Long) = new Wrap(l.toString+"L")

class User {
  private[this] var myData = ""
  def set(w: Wrap) { println("Setting to "+w.data); myData = w.data }
}

val u = new User
u.set(true)
u.set(50L)
u.set(50)     // Int gets upconverted to Long for free, so this works
u.set("Fish")
// u.set(3.14159)  // This is a type mismatch

这有点像取Any,除了您可以限制您喜欢的类型,并指定转换为您想到的任何通用表示。但是,如果没有通用形式,那么我不确定你的意思是代码每次都做同样的事情。 (也许你的意思是你可以设想一个自动生成代码的宏(或其他程序) - Scala没有内置的支持,但你当然可以写一个生成Scala代码的Scala程序。)< / p>

答案 3 :(得分:0)

回顾迄今为止收集的结果,建议采用以下几种解决方案:

  • 使用模式匹配(导致处理不同参数类型的不同策略的碎片化)
  • 使用反射(对于理想情况下应该超快的东西来说是昂贵的)
  • ...并添加我最终实现的那个:为每种类型的参数编写一个适配器。

为了更准确一点,整个练习是围绕京都内阁写一个包装纸。 Kyoto Cabinet具有将字节数组键与字节数组值相关联的方法以及具有字符串值的字符串键。然后它基本上复制了大多数处理字节数组和字符串的键和值的操作。

为了在Kyoto Cabinet的DB类周围创建一个Map包装器,我定义了一个特性TypedDBOperations [T],其中T是参数的类型,并且实现了两次。如果我现在构造一个Map [A​​rray [Byte],Array [Byte]],隐式转换将自动为其分配TypedDBOperations的正确instane,调用DB类的基于Array [Byte]的操作。

这是我一直在谈论的特质:

trait TypedDBOperations[K,V] {
  def get(db: DB, key: K): V
  def set(db: DB, key: K, value: V): Boolean
  def remove(db: DB, key: K): Boolean
  def get(cursor: Cursor): (K, V)
}

这些是两种键值组合的实现:

  implicit object StringDBOperations extends TypedDBOperations[String] {
    def get(cursor: Cursor) = {
      val Array(a, b) = cursor.get_str(false)
      (a, b)
    }
    def remove(db: DB, key: String) = db.remove(key)
    def set(db: DB, key: String, value: String) = db.set(key, value)
    def get(db: DB, key: String) = db.get(key)
  }

  implicit object ByteArrayOperations extends TypedDBOperations[Array[Byte]] {
    def get(cursor: Cursor) = {
      val Array(a, b) = cursor.get(false)
      (a, b)
    }
    def remove(db: DB, key: Array[Byte]) = db.remove(key)
    def set(db: DB, key: Array[Byte], value: Array[Byte]) = db.set(key, value)
    def get(db: DB, key: Array[Byte]) = db.get(key)
  }

不是最令人满意的解决方案,但它完成了工作。再说一遍,注意到仍有相当多的重复,但似乎没有办法摆脱它。