From Scala, I'm using a Java library that expects a class argument. Example:
def service: OAuthService = new ServiceBuilder()
.provider(classOf[RunApi])
RunApi is Java class.
I'd like to be able pass a variety of classes to provider though. I have a list of them in String format.
Example, if I know the RunApi in String format; e.g. "com.me.RunApi", how can construct the equivalent to above code?
Use forName method:
scala> Class.forName("scala.collection.mutable.ArrayBuffer")
res0: java.lang.Class[_] = class scala.collection.mutable.ArrayBuffer
val yourInstance: ClassOrTrait = Class.forName("Package.ClassName").newInstance().asInstanceOf[ClassOrTrait]
Related
I am trying use a custom annotation in Scala. In this example, I create a string that I want to annotate with metadata (in this case, another string). Then, given an instance of the data, and I want to read the annotation.
scala> case class named(name: String) extends scala.annotation.StaticAnnotation
defined class named
scala> #named("Greeting") val v = "Hello"
v: String = Hello
scala> def valueToName(x: String): String = ???
valueToName: (x: String)String
scala> valueToName(v) // returns "Greeting"
Is this even possible?
With scala 2.11.6, this works to extract values of a annotation:
case class Named(name: String) extends scala.annotation.StaticAnnotation
val myAnnotatedClass: ClassSymbol = u.runtimeMirror(Thread.currentThread().getContextClassLoader).staticClass("MyAnnotatedClass")
val annotation: Option[Annotation] = myAnnotatedClass.annotations.find(_.tree.tpe =:= u.typeOf[Named])
val result = annotation.flatMap { a =>
a.tree.children.tail.collect({ case Literal(Constant(name: String)) => doSomething(name) }).headOption
}
There are different kinds of annotations in Scala:
Java Annotations that you can access using the Java Reflection API, annotations that are just in the source code, static annotations that are available to the type checker across different compilation units (so they should be somewhere in a class file but not where normal reflections go) and classfile annotations which are stored like java annotations, but cannot be read using the java reflection api.
I have described how to access static and classfile annotations here: What is the (current) state of scala reflection capabilities, especially wrt annotations, as of version 2.11?
If you just need a annotation containing a string using a Java annotation that is loaded by the JVM for you might be the simpler alternative.
Code sample illustrating what I'm aiming for:
import scala.reflect.ClassTag
class C[A](implicit ct: ClassTag[A]) {
def inst: A = ct.runtimeClass.newInstance.asInstanceOf[A]
}
val c = new C[String]
c.inst
//=> res8: String = ""
// So far so good
val className = "C"
val typeParam = "String"
How do I use className and typeParam to obtain an instance of C[String] at runtime (using Scala 2.11.5)?
Hum ... That looks very suspicious, but anyway, you should be able to get it like this:
Class.forName(className).getConstructor(classOf[ClassTag[_]]).newInstance(
ClassTag(Class.forName(typeParam)))
You'll need fully qualified names in className and typeParam.
The class referenced by className must have a constructor with one parameter of type ClassTag.
It will blow up if anything's amiss.
What do you think you need this for? There should be a better to achieve your end goal.
In scala, I want to be able to say
val user = Node.create[User](...) // return User object
So here's what I have so far:
def create[T : TypeTag](map: Map[String, Any]) {
val type = typeOf[T]
// create class from type here???
}
I've been digging around how to create classes from generic types and found out that using ClassManifest seems to be deprecated. Instead, type tags are here, so I'm able to do something like this typeOf[T] and actually get the type.. but then I'm lost. If I could get the class, then I could use something like class.newInstance and manually set the fields from there.
Question is: given a type, can I get a class instance of the given type?
The easiest way in fact is to use ClassTag:
def create[T : ClassTag](map: Map[String, Any]): T = {
val clazz: Class[_] = classTag[T].runtimeClass
clazz.newInstance(<constructor arguments here>).asInstanceOf[T]
}
ClassTag is a thin wrapper around Java Class, primarily used for arrays instantiation.
TypeTag facility is more powerful. First, you can use it to invoke Java reflection:
import scala.reflect.runtime.universe._
def create[T: TypeTag](map: Map[String, Any]): T = {
val mirror = runtimeMirror(getClass.getClassLoader) // current class classloader
val clazz: Class[_] = mirror.runtimeClass(typeOf[T].typeSymbol.asClass)
clazz.newInstance(<constructor arguments here>).asInstanceOf[T]
}
However, Scala reflection allows to instantiate classes without dropping back to Java reflection:
def create[T: TypeTag](map: Map[String, Any]): T = {
// obtain type symbol for the class, it is like Class but for Scala types
val typeSym = typeOf[T].typeSymbol.asClass
// obtain class mirror using runtime mirror for the given classloader
val mirror = runtimeMirror(getClass.getClassLoader) // current class classloader
val cm = mirror.reflectClass(typeSym)
// resolve class constructor using class mirror and
// a constructor declaration on the type symbol
val ctor = typeSym.decl(termNames.CONSTRUCTOR).asMethod
val ctorm = cm.reflectConstructor(cm)
// invoke the constructor
ctorm(<constructor arguments here>).asInstanceOf[T]
}
If you want to create a class with overloaded constructors, it may require more work though - you'll have to select correct constructor from declarations list, but the basic idea is the same. You can read more on Scala reflection here
There is a way to do it with reflection: either runtime reflection, or in a macro. Regarding runtime reflection way, you can have a look at my blog post where I tried to do something like what you are trying to do now. Using compile-time reflection with macros might be a better option, depending on your need.
How does one invoke the following java method from scala?
public static Config parseMap(Map<String, ? extends Object> values,
String originDescription) {
I have attempted to invoke it as follows:
val SAMPLE_PROPS_MAP : Map[String,AnyRef] = Map("hiveSaveFreq" -> new java.lang.Long(10L), "aggSeconds" -> new java.lang.Long(3))
val props = ConfigFactory.parseMap(SAMPLE_PROPS_MAP,"props"))
However Intellij complains:
Cannot resolve symbol parseMap
To ensure this were not other issues (e.g. incorrect version of libraries):
val props = ConfigFactory.parseMap(null,"props"))
works fine. Also, the intellisense bubble does confirm the signature as being shown above (having Map).
However Intellij complains:
So then what is the Scala equivalent to the ? extends Object
The issue is not on the ? extends Object type boundary declaration but rather on the Map definition. The signature you have is:
public static Config parseMap(**java.util.Map**<String, ? extends Object> values, String originDescription)
and you are passing it a scala.collection.immutable.Map
You need to convert your scala map to a java map, either by creating a java map "by hand" or using the facilities in the scala API:
import scala.collection.JavaConverters._
val SamplePropsMap : Map[String,AnyRef] = Map("hiveSaveFreq" -> new java.lang.Long(10L), "aggSeconds" -> new java.lang.Long(3))
val props = ConfigFactory.parseMap(SamplePropsMap.asJava,"props"))
(note: The implicit methods in JavaConverters are the recommended way to deal with java-scala conversions, by using javaCollection.asScala and scalaCollection.asJava. Also note that in Scala, the convention for 'constants' is camel-case starting with a capital. e.g. CamelCase)
The Map needs to be converted to java format, as follows:
import collection.JavaConversions._
val props = ConfigFactory.parseMap(mapAsJavaMap(SAMPLE_PROPS_MAP))
Hi I am trying to extract a JSON using reflection
import net.liftweb.json._
case class Bike(make: String, price: Int) {
def this(price: Int) = this("Trek", price)
}
val cls = Class.forName("Bike")
val manifest = Manifest.classType(cls)
val parsedData =net.liftweb.json.JsonParser.parse(json)
JsonParser.parse(""" {"price":350} """).extract[manifest]
however I am getting this error:
not found: type manifest
JsonParser.parse(""" {"price":350} """).extract[manifest]
^
although manifest is from type Manifest
You can extract directly into a case class
val json = "the json";
val bike = parse(json).extract[Bike];
JSON parsing is done through reflection.
If the class is a runtime construct, create a TypeInfo instance and pass that to the extract method.
There is a variation of the extract() method that might work for you, if you provide it with a TypeInfo instance.
See here: https://github.com/lift/lift/blob/master/framework/lift-base/lift-json/src/main/scala/net/liftweb/json/Extraction.scala#L178