Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - obtaining a class object from a generic type

Tags:

Is it possible to create a Class object purely from a generic parameter? For example:

class myclass[T] { 
  def something(): Class[_ <: T] = 
    classOf[T] //this doesn't work
}

Since the type will have been erased at runtime, it seems like this a job for manifests, but I haven't found an example that demonstrates this particular usage. I tried the following, but it doesn't work either:

class myclass[T] { 
  def something()(implicit m: Manifest[T]): Class[_ <: T] = 
    m.erasure //this doesn't work
}

I suspect this failure is due to, as the API points out, there is no subtype relationship between the type of m.erasure's result and T.

EDIT: I'm not really interested in what the type T is, I just need an object of type Class[_ <: T] to pass to a method in the hadoop framework.

Any pointers?

like image 993
dhg Avatar asked Nov 21 '11 06:11

dhg


People also ask

How do I use generic in Scala?

To use a generic class, put the type in the square brackets in place of A . Class Apple and Banana both extend Fruit so we can push instances apple and banana onto the stack of Fruit . Note: subtyping of generic types is *invariant*.

Can a generic can take any object data type?

Generics in Java is similar to templates in C++. The idea is to allow type (Integer, String, … etc and user defined types) to be a parameter to methods, classes and interfaces. For example, classes like HashSet, ArrayList, HashMap, etc use generics very well. We can use them for any type.

Does Scala support generics?

Most Scala generic classes are collections, such as the immutable List, Queue, Set, Map, or their mutable equivalents, and Stack. Collections are containers of zero or more objects. We also have generic containers that aren't so obvious at first.

What is classOf in Scala?

A classOf[T] is a value of type Class[T] . In other words, classOf[T]: Class[T] . For example: scala> val strClass = classOf[String] strClass: Class[String] = class java. lang. String scala> :t strClass Class[String]


2 Answers

def myClassOf[T:ClassTag] = implicitly[ClassTag[T]].runtimeClass
like image 150
Ali Salehi Avatar answered Sep 17 '22 13:09

Ali Salehi


You can cast the result of m.erasure to a Class[T]:

class myclass[T] { 
    def something()(implicit m: Manifest[T]): Class[T] = 
        m.erasure.asInstanceOf[Class[T]]
}

This works fine for basic (non-generic) types:

scala> new myclass[String]().something()
res5: Class[String] = class java.lang.String

But note what happens if I use an instantiated type constructor like List[String] for T:

scala> new myclass[List[String]]().something()
res6: Class[List[String]] = class scala.collection.immutable.List

Due to erasure, there is only one Class object for all the possible instantiations of a given type constructor.

Edit

I'm not sure why Manifest[T].erasure returns Class[_] instead of Class[T], but if I had to speculate, I would say it's to discourage you from using the methods on Class which allow you to compare two classes for equality or a subtype relationship, since those methods will give you wrong answers when the Class is parameterized with an instantiated generic type.

For example,

scala> classOf[List[String]] == classOf[List[Int]]
res25: Boolean = true

scala> classOf[List[String]].isAssignableFrom(classOf[List[Int]])
res26: Boolean = true

These results might surprise you and/or lead to a bug in your program. Instead of comparing classes this way, you should normally just pass around Manifests instead and compare them, since they have more information*:

scala> manifest[List[String]] == manifest[List[Int]]
res27: Boolean = false

scala> manifest[List[String]] >:> manifest[List[Int]]
res28: Boolean = false

As I understand it, Manifests are meant to supersede Classes for most use cases... but of course, if you're using a framework that requires a Class, there's not much choice. I would suppose that the imposition of casting the result of erasure is just a sort of "acknowledgement of liability" that you're using an inferior product at your own risk :)

* Note that, as the documentation for Manifest says, these manifest comparison operators "should be considered approximations only, as there are numerous aspects of type conformance which are not yet adequately represented in manifests."

like image 31
Tom Crockett Avatar answered Sep 19 '22 13:09

Tom Crockett