Тот же метод, разные типы аргументов, реализованные за один раз - PullRequest
0 голосов
/ 09 января 2011

У меня есть класс (Java) с такими операциями:

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

По сути, все выполняют одну и ту же задачу, но просто принимают разные типы аргументов.Я хотел бы создать универсальный 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 ]

3 голосов
/ 10 января 2011

Использовать отражение.

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.

2 голосов
/ 09 января 2011

Я не совсем понимаю ваш вариант использования, но вы можете попытаться сделать одну вещь - если производительность не имеет первостепенного значения - это создать класс-оболочку, который преобразуется в универсальную форму для вас, и затем иметь всеваши методы принимают этот класс-обертку (с соответствующими неявными преобразованиями).Например:

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.)

2 голосов
/ 09 января 2011

Я думаю, что сопоставление должно работать хорошо

def set(value: T) {
    value match {
         case s: String => holder.set(s)
         case i: Int => holder.set(i)
         case b: Boolean => holder.set(b)
    }
}
0 голосов
/ 25 февраля 2011

Обращаясь к результатам, собранным до сих пор, предлагается пара решений:

  • Использовать сопоставление с образцом (приводит к фрагментации различных стратегий работы с различными типами параметров)
  • Используйте отражение (слишком дорого для чего-то, что в идеале должно быть очень быстро )
  • ... и добавление того, что я в итоге реализовал: написать адаптер для каждого типа параметра.

Чтобы быть более точным, все упражнение было посвящено написанию обертки вокруг кабинета Киото. В Kyoto Cabinet есть методы для привязки ключей байтового массива к значениям байтового массива и ключей String со значениями String. И затем он в основном копирует большинство операций для работы с ключами и значениями как для байтового массива, так и для строк.

Чтобы создать оболочку Map вокруг класса DB кабинета Киото, я определил черту TypedDBOperations [T], где T - тип параметра, и реализовал его дважды. Если я сейчас создам Map [Array [Byte], Array [Byte]], неявное преобразование автоматически назначит ему соответствующий экземпляр TypedDBOperations, вызывая основанные на Array [Byte] операции класса DB.

Это та черта, о которой я говорил:

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)
  }

Не самое удачное решение, но оно выполняет свою работу. Опять же, обратите внимание, что дублирования все еще немного, но, похоже, от него не избавиться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...