Как читать класс объекта Scala, расширяющего Any, но не AnyRef? - PullRequest
6 голосов
/ 10 апреля 2011

У меня есть гетерогенный список, подобный следующему:

val l = List(1, "One", true)

, и мне нужно отфильтровать его объекты, извлекая только те объекты, которые принадлежат данному классу.Для этой цели я написал очень простой метод, подобный этому:

def filterByClass[A](l: List[_], c: Class[A]) =
  l filter (_.asInstanceOf[AnyRef].getClass() == c)

Обратите внимание, что я обязан добавить явное преобразование в AnyRef, чтобы избежать этой проблемы компиляции:

error: type mismatch;
found   : _$1 where type _$1
required: ?{val getClass(): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method any2stringadd in object Predef of type (x: Any)scala.runtime.StringAdd
and method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
are possible conversion functions from _$1 to ?{val getClass(): ?}
l filter (_.getClass() == c)

Однако, таким образом, вызов:

filterByClass(l, classOf[String])

возвращается, как и ожидалось:

List(One)

, но, конечно, то же самое не работает, например, с Int, поскольку они расширяют Any, ноне AnyRef, поэтому, вызвав:

filterByClass(l, classOf[Int])

результатом будет просто пустой список.

Есть ли способ заставить мой метод filterByClass работать даже с Int, Boolean и всеми другими классамиРасширение Any?

Ответы [ 6 ]

13 голосов
/ 10 апреля 2011

Метод collect уже делает то, что вы хотите. Например, чтобы собрать все Int в коллекции, вы можете написать

xs collect { case x: Int => x }

Это, конечно, работает только тогда, когда вы жестко кодируете тип, но поскольку примитивы обрабатываются не так, как ссылочные типы, на самом деле это лучше сделать. Вы можете сделать свою жизнь проще с некоторыми типами классов:

case class Collect[A](collect: PartialFunction[Any,A])

object Collect {

  implicit val collectInt: Collect[Int] = Collect[Int]({case x: Int => x})

  // repeat for other primitives

  // for types that extend AnyRef
  implicit def collectAnyRef[A <: AnyRef](implicit mf: ClassManifest[A]) =
    Collect[A]({ case x if mf.erasure.isInstance(x) => x.asInstanceOf[A] })
}

def collectInstance[A : Collect](xs: List[_ >: A]) =
  xs.collect(implicitly[Collect[A]].collect)

Тогда вы можете использовать его, даже не передавая Class[A] экземпляр:

scala> collectInstance[Int](l)
res5: List[Int] = List(1)

scala> collectInstance[String](l)
res6: List[String] = List(One)
3 голосов
/ 10 апреля 2011

Использование isInstanceOf:

scala> val l = List(1, "One", 2)
l: List[Any] = List(1, One, 2)

scala> l . filter(_.isInstanceOf[String])
res1: List[Any] = List(One)

scala> l . filter(_.isInstanceOf[Int])
res2: List[Any] = List(1, 2)

edit: По требованию OP, есть еще одна версия, которая перемещает проверку в методе.Я не смог найти способ использовать isInstanceOf, и поэтому я изменил реализацию для использования ClassManifest:

def filterByClass[A](l: List[_])(implicit mf: ClassManifest[A]) =
  l.filter(mf.erasure.isInstance(_))

Некоторые сценарии использования:

scala> filterByClass[String](l)
res5: List[Any] = List(One)

scala> filterByClass[java.lang.Integer](l)
res6: List[Any] = List(1, 2)

scala> filterByClass[Int](l)
res7: List[Any] = List()

Как видно выше, эторешение не работает с типом Int Scala.

1 голос
/ 12 апреля 2011

Класс элемента в List [Any] никогда не является classOf [Int], поэтому он ведет себя как ожидалось.Ваши предположения, очевидно, оставляют это неожиданным, но трудно дать вам лучший путь, потому что правильный путь - «не делайте этого».гетерогенный список?Может быть, это иллюстративно.Мне интересно, как вы думаете, Java делает это лучше.

scala> def f[T: Manifest](xs: List[T]) = println(manifest[T] + ", " + manifest[T].erasure)
f: [T](xs: List[T])(implicit evidence$1: Manifest[T])Unit

scala> f(List(1))
Int, int

scala> f(List(1, true))
AnyVal, class java.lang.Object

scala> f(List(1, "One", true))
Any, class java.lang.Object
0 голосов
/ 14 апреля 2011

В конце эта проблема уменьшается, чтобы найти карту между примитивом и соответствующим коробочным типом.Может быть, помощь может прийти из scala.reflect.Invocation (не входит в окончательную версию 2.8.0), в частности, из функции getAnyValClass (здесь слегка отредактировано)

def getAnyValClass(x: Any): java.lang.Class[_] = x match {
  case _: Byte    => classOf[Byte]
  case _: Short   => classOf[Short]
  case _: Int     => classOf[Int]
  case _: Long    => classOf[Long]
  case _: Float   => classOf[Float]
  case _: Double  => classOf[Double]
  case _: Char    => classOf[Char]
  case _: Boolean => classOf[Boolean]
  case _: Unit    => classOf[Unit]
  case x@_        => x.asInstanceOf[AnyRef].getClass
}

С этой функцией фильтрпросто как

def filterByClass[T: Manifest](l:List[Any]) = {
  l filter (getAnyValClass(_) == manifest[T].erasure)
}

и вызов:

filterByClass[Int](List(1,"one",true))
0 голосов
/ 14 апреля 2011

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

private def normalizeClass(c: Class[_]): Class[_] =
  if (classOf[AnyRef].isAssignableFrom((c))) c
  else if (c == classOf[Int]) classOf[java.lang.Integer]
  // Add all other primitive types
  else classOf[java.lang.Boolean]

Итак, используя его в моем предыдущем методе filterByClass следующим образом:

def filterByClass[A](l: List[_], c: Class[A]) =
  l filter (normalizeClass(c).isInstance(_))

вызов:

filterByClass(List(1, "One", false), classOf[Int])

просто возвращает

List(1)

, как и ожидалось.

0 голосов
/ 10 апреля 2011

Это сработало для меня.Это то, что вы хотите?

scala> val l = List(1, "One", true)
l: List[Any] = List(1, One, true)

scala> l filter { case x: String => true; case _ => false }
res0: List[Any] = List(One)

scala> l filter { case x: Int => true; case _ => false }
res1: List[Any] = List(1)

scala> l filter { case x: Boolean => true; case _ => false }
res2: List[Any] = List(true)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...