如何读取扩展 Any 但不扩展 AnyRef 的 Scala 对象的类?

发布于 2024-10-31 04:28:27 字数 1081 浏览 1 评论 0原文

我有一个如下所示的异构列表:

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

结果只是空 List。

有没有办法让我的 filterByClass 方法甚至可以与 Int、Boolean 和扩展 Any 的所有其他类一起使用?

I have an heterogeneous List like the following one:

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

and I need to filter its objects by extracting only the ones belonging to a given Class. For this purpose I wrote a very simple method like this:

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

Note that I am obliged to add the explicit conversion to AnyRef in order to avoid this compilation problem:

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)

However in this way the invocation of:

filterByClass(l, classOf[String])

returns as expected:

List(One)

but of course the same doesn't work, for example, with Int since they extends Any but not AnyRef, so by invoking:

filterByClass(l, classOf[Int])

the result is just the empty List.

Is there a way to make my filterByClass method working even with Int, Boolean and all the other classes extending Any?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

倾其所爱 2024-11-07 04:28:27

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)

The collect method already does what you want. For example to collect all Ints in a collection you could write

xs collect { case x: Int => x }

This of course only works when you hardcode the type but as primitives are handled differently from reference types it is actually better to do so. You can make your life easier with some type classes:

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)

Then you can use it without even passing a Class[A] instance:

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

scala> collectInstance[String](l)
res6: List[String] = List(One)
月隐月明月朦胧 2024-11-07 04:28:27

使用 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)

编辑:
根据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()

如上所示,此解决方案不适用于 Scala 的 Int 类型。

Using 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:
As the OP requested, here's another version that moves the check in a method. I Couldn't find a way to use isInstanceOf and so I changed the implementation to use a ClassManifest:

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

Some usage scenarios:

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

As can be seen above, this solution doesn't work with Scala's Int type.

诺曦 2024-11-07 04:28:27

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

The class of an element in a List[Any] is never classOf[Int], so this is behaving as expected. Your assumptions apparently leave this unexpected, but it's hard to give you a better way because the right way is "don't do that."

What do you think can be said about the classes of the members of a heterogenous list? Maybe this is illustrative. I'm curious how you think java does it better.

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
堇色安年 2024-11-07 04:28:27

这对我有用。这是你想要的吗?

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)

This worked for me. Is this what you want?

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)
心不设防 2024-11-07 04:28:27

尽管我的解决方案可能不如 这个我发现我的更快更容易。我刚刚定义了一个这样的方法:

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)

只是按预期

Despite my solution could be less elegant than this one I find mine quicker and easier. I just defined a method like this:

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]

So by using it in my former filterByClass method as it follows:

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

the invocation of:

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

just returns

List(1)

as expected.

怎会甘心 2024-11-07 04:28:27

最后,这个问题简化为找到基元和相应的装箱类型之间的映射。
也许可以从 scala.reflect.Inspiration (未包含在 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))

At the end, this problem reduces to find a map between a primitive and the corresponding boxed type.
Maybe a help can arrive from scala.reflect.Invocation (not included in the final version of 2.8.0), the getAnyValClass function in particular (here slightly edited)

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
}

With this function the filter is as easy as

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

and the invocation is:

filterByClass[Int](List(1,"one",true))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文