How to read a class of a Scala object extending Any, but not AnyRef?

I have a heterogeneous list similar to the following:

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

and I need to filter out its objects, extracting only those that belong to this class. For this, I wrote a very simple way:

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

Note that I must add an explicit conversion to AnyRef 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 call:

 filterByClass(l, classOf[String]) 

returns as expected:

 List(One) 

but, of course, this does not work, for example, with Int, since they extend Any, but not AnyRef, therefore, by calling:

 filterByClass(l, classOf[Int]) 

the result is only an empty list.

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

+6
source share
6 answers

Although my solution may be less elegant than this , I find mine faster 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, using this in my previous filterByClass method, as it follows:

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

call:

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

just returns

 List(1) 

as was expected.

0
source

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

 xs collect { case x: Int => x } 

This, of course, only works with hard type coding, but since primitives are handled differently than reference types, this is actually better. 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 an instance of Class[A] :

 scala> collectInstance[Int](l) res5: List[Int] = List(1) scala> collectInstance[String](l) res6: List[String] = List(One) 
+13
source

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 requested by OP, here is another version that moves validation in a method. I could not find a way to use isInstanceOf, so I changed the implementation to use ClassManifest:

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

Some use cases:

 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 you can see above, this solution does not work with the Scala Int type.

+3
source

The element class in List [Any] is never the class Of [Int], so it behaves as expected. Your assumptions seem to leave this unexpected, but it’s hard to give you a better way, because the right way is β€œdon't do it.”

What do you think about the classes of members of a heterogeneous list? Perhaps this is clear. 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 
+1
source

It 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) 
0
source

In the end, this problem boils down to finding a map between the primitive and the corresponding box type. Maybe help can come from scala.reflect.Invocation (not included in the final version 2.8.0), in particular, the getAnyValClass function (slightly edited here)

 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 } 

Using this function, a filter is as simple as

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

and the call:

 filterByClass[Int](List(1,"one",true)) 
0
source

Source: https://habr.com/ru/post/885603/


All Articles