class Test(a: String, b: Array[String], c: Array[String]){
def this(b: Array[String], c: Array[String]) {
this("1", b, c)
}
def this() = {
this(null, null, null)
}
}
I have a class Test like above, and I would like use scala reflection to invoke one of them
I try to use the follow code
import scala.reflect.runtime.{universe => ru}
val clsTest = ru.typeOf[Test].typeSymbol.asClass
val cm = m.reflectClass(clsTest)
val ctor = ru.typeOf[Test].decl(ru.termNames.CONSTRUCTOR).asTerm.alternatives.map(_.asMethod)
but I don't know how to select the method based on method signature.
Is there any approach to select the method based on the type signature like java reflect code? Thanks!
I have read the scala doc about reflect, but it don't solve my problem. it has only one constructor method. scala reflect doc
I have found a approach to filter by parameter types
methodName: means which method you want to reflect
allScope: true means find from this and super, false means find only from this
types: is the parameter types, you can use Seq(typeOf[T1], typeOf[T2]):_*
x: is the instance which will be reflected
the key is we can get method parameter types by methodSymbol.paramLists.head.map(_.info)
val ru: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
def m: ru.Mirror = {
ru.runtimeMirror(Thread.currentThread().getContextClassLoader)
}
//reflect method, method can't be curry
def reflectMethod[T: ru.TypeTag : ClassTag](methodName: String, allScope: Boolean, types: ru.Type*)(x: T): ru.MethodMirror = {
val instanceMirror = m.reflect(x)
val methodSymbols = if (allScope) {
val members = getTypeTag(x).tpe.member(ru.TermName(methodName))
if (members.equals(ru.NoSymbol)) {
throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x))
}
members
.asTerm
.alternatives
.map(_.asMethod)
} else {
val decls = getTypeTag(x).tpe.decl(ru.TermName(methodName))
if (decls.equals(ru.NoSymbol)) {
throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x))
}
decls
.asTerm
.alternatives
.map(_.asMethod)
}
methodSymbols.foreach(item => assert(item.paramLists.size < 2, "we don't support curry method yet"))
val methodSymbol = methodSymbols.find(item =>
if (item.paramLists.head.isEmpty) {
types.isEmpty
} else {
if (types.isEmpty) {
item.paramLists.head.isEmpty
} else {
// empty forall is true
item.paramLists.head.zip(types).forall(pair => pair._1.info =:= pair._2)
}
}).getOrElse(throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x)))
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror
}
private def noSuchMethodException[T: ru.TypeTag : ClassTag](methodName: String, allScope: Boolean, types: ru.Type*)(x: T): String = {
s"no such method: $methodName, allScope: $allScope type: $types in ${getRuntimeClass(x)}"
}
// the method as List(list(a,b,c))
// and this is PrimaryConstructor
class Test(a: String, b: Array[String], c: Array[String]) {
// the method as List(list(b,c))
def this(b: Array[String], c: Array[String]) {
this("1", b, c)
}
// the method as List(list())
def this() = {
this(null, null, null)
}
// the method as List(list(a),list(b,c)
def this(a:String )(b:String,c:String ){
this(null,null,null)
}
}
val constructor = typeOf[Test].members
// filter all constructor
.filter(e => e.isConstructor).map(e => e.asMethod)
// find which are you want
// edit 1
.find( e =>{
val methodParamsType = e.paramLists.head.map(e =>e.typeSignature)
// what params type are you
val expectParamsType = List(typeOf[Array[String]],typeOf[Array[String]])
methodParamsType.length == expectParamsType.length &&
methodParamsType.zip(expectParamsType).forall{case (l,r)=>l =:= r }
})
// or
// .find(e=>e.isPrimaryConstructor)
// .find(e=>e.paramLists.head.length == 2)
.get
Related
Please consider the following trivial example:
class C[P](val list: Seq[P]){
def print(e: P){
println(e)
}
}
object Test{
def g[P](c: C[P]) = {
c.print(c.list(0))
}
def f(i: Int): C[_] = {
i match {
case 1 => new C(Seq(1, 2, 3))
case _ => new C(Seq("A", "B", "C"))
}
}
def main(args: Array[String]): Unit = {
val c = f(1)
g(c) // works
c.print(c.list(0)) // does not work
}
}
My question is, in the main function, why the first call compiles, but the second gives "type mismatch" error.
Is there any other (better) way to do what is intended here?
Edit 1:
According to the answer by #chengpohi, I can change the type of the returned value of f to C[Any], but this generally may not work. For example, if we change the code to
class B[P]
class BInt extends B[Int]
class BString extends B[String]
class C[P](val list: Seq[P], val obj: B[P]) {
def print(e: P) {
println(e)
}
}
object Test {
def g[P](c: C[P]) = {
c.print(c.list(0))
}
def f(i: Int): C[_] = {
i match {
case 1 => new C(Seq(1), new BInt)
case _ => new C(Seq("String"), new BString)
}
}
def main(args: Array[String]): Unit = {
val c = f(1)
g(c) // works
c.print(c.list(0)) // does not work
}
}
I cannot change the return type of f to C[Any] anymore ("type mismatch").
def f(i: Int): C[Any] = {
i match {
case 1 => new C(Seq(1, 2, 3))
case _ => new C(Seq("A", "B", "C"))
}
}
Try to set the f method return type: C[Any] from C[_], for typeC[_], the compiler will translate C[_] to C<?>.
for:
def g[P](c: C[P]) = {
c.print(c.list(0))
}
this method works, this is caused by that we have bound P type in this method g, so the compiler can infer this generic P (Object type).
but in main method:
c.print(c.list(0))
There is no type context for c, and c's type is C[_], but c.list's type is Seq[Any], and for the Generic Type P in the c.print will be thought as _$1 type. so the type mismatch compile error throwed.
You can use type variable patterns to give a name to the type parameter:
f(1) match {
case c: C[a] => c.print(c.list(0))
}
Is there a simple way to return a concrete type in an override method? And what about creating an instance of a concrete implementation? And calling chained methods implemented in the concrete class, so they return a correct type, too? I have a solution (based on https://stackoverflow.com/a/14905650) but I feel these things should be simpler.
There are many similar questions, but everyone's case is a little different, so here is another example (shortened from https://github.com/valdanylchuk/saiml/tree/master/src/main/scala/saiml/ga). When replying, if possible, please check if the whole code block compiles with your suggested change, because there are subtle cascading effects. I could not make this work with the "curiously recurring template pattern", for example (not that I find it nicer).
import scala.reflect.ClassTag
import scala.util.Random
abstract class Individual(val genome: String) {
type Self
def this() = this("") // please override with a random constructor
def crossover(that: Individual): Self
}
class HelloGenetic(override val genome: String) extends Individual {
type Self = HelloGenetic
def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString)
override def crossover(that: Individual): HelloGenetic = {
val newGenome = this.genome.substring(0, 6) + that.genome.substring(6)
new HelloGenetic(newGenome)
}
}
class Population[A <: Individual {type Self = A} :ClassTag]( val size: Int,
tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) {
val individuals: Vector[A] = givenIndividuals getOrElse
Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A])
def tournamentSelect(): A = individuals.head // not really, skipped
def evolve: Population[A] = {
val nextGen = (0 until size).map { _ =>
val parent1: A = tournamentSelect()
val parent2: A = tournamentSelect()
val child: A = parent1.crossover(parent2)
child
}.toVector
new Population(size, tournamentSize, Some(nextGen))
}
}
class Genetic[A <: Individual {type Self = A} :ClassTag](populationSize: Int, tournamentSize: Int) {
def optimize(maxGen: Int, maxMillis: Long): Individual = {
val first = new Population[A](populationSize, tournamentSize)
val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve }
optPop.individuals.head
}
}
The CRTP version is
abstract class Individual[A <: Individual[A]](val genome: String) {
def this() = this("") // please override with a random constructor
def crossover(that: A): A
}
class HelloGenetic(override val genome: String) extends Individual[HelloGenetic] {
def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString)
override def crossover(that: HelloGenetic): HelloGenetic = {
val newGenome = this.genome.substring(0, 6) + that.genome.substring(6)
new HelloGenetic(newGenome)
}
}
class Population[A <: Individual[A] :ClassTag]( val size: Int,
tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) {
val individuals: Vector[A] = givenIndividuals getOrElse
Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A])
def tournamentSelect(): A = individuals.head // not really, skipped
def evolve: Population[A] = {
val nextGen = (0 until size).map { _ =>
val parent1: A = tournamentSelect()
val parent2: A = tournamentSelect()
val child: A = parent1.crossover(parent2)
child
}.toVector
new Population(size, tournamentSize, Some(nextGen))
}
}
class Genetic[A <: Individual[A] :ClassTag](populationSize: Int, tournamentSize: Int) {
def optimize(maxGen: Int, maxMillis: Long): Individual[A] = {
val first = new Population[A](populationSize, tournamentSize)
val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve }
optPop.individuals.head
}
}
which compiles. For creating the instances, I'd suggest just passing functions:
class Population[A <: Individual[A]](val size: Int,
tournamentSize: Int, makeIndividual: () => A, givenIndividuals: Option[Vector[A]] = None) {
val individuals: Vector[A] = givenIndividuals getOrElse
Vector.fill(size)(makeIndividual())
...
}
If you want to pass them implicitly, you can easily do so:
trait IndividualFactory[A] {
def apply(): A
}
class HelloGenetic ... // remove def this()
object HelloGenetic {
implicit val factory: IndividualFactory[HelloGenetic] = new IndividualFactory[HelloGenetic] {
def apply() = new HelloGenetic(Random.alphanumeric.take("Hello, World!".length).mkString)
}
}
class Population[A <: Individual[A]](val size: Int,
tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None)
(implicit factory: IndividualFactory[A]) {
val individuals: Vector[A] = givenIndividuals getOrElse
Vector.fill(size)(factory())
...
}
Do i have a misunderstanding how implciits work in Scala - given the following trait in Scala,
trait hasConfig {
implicit def string2array(s: java.lang.String): Array[String] = {
LoadedProperties.getList(s)
}
implicit def string2boolean(s: java.lang.String) : java.lang.Boolean = {
s.toLowerCase() match {
case "true" => true
case "false" => false
}
}
var config: Properties = new Properties()
def getConfigs : Properties = config
def loadConfigs(prop:Properties) : Properties = {
config = prop
config
}
def getConfigAs[T](key:String):T = {
if (hasConfig(key)) {
val value : T = config.getProperty(key).asInstanceOf[T]
value
}
else throw new Exception("Key not found in config")
}
def hasConfig(key: String): Boolean = {
config.containsKey(k)
}
}
Though java.util.properties contains (String, String) key value pairs, I expect the following code to work due to the implicit converstion defined,
class hasConfigTest extends FunSuite {
val recModel = new Object with hasConfig
//val prop = LoadedProperties.fromFile("test") Read properties from some file
recModel.loadConfigs(prop)
test("test string paramater") {
assert(recModel.getConfigAs[String]("application.id").equals("framework"))
}
test("test boolean paramater") {
assert(recModel.getConfigAs[Boolean]("framework.booleanvalue") == true)
//Property file contains framework.booleanvalue=true
//expected to return java.lang.boolean, get java.lang.string
}
}
However, I get the following error,
java.lang.String cannot be cast to java.lang.Boolean
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
Why is the implcit conversion not taking care of this?
It doesn't work because casting (asInstanceOf) is something entirely different than implicit conversions. There are multiple ways in which you can solve this.
Implicit conversion
If you want to use the hardcore implicit conversions magic you should rewrite you getConfigAs method like this:
def getConfig(key:String): String = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
value
}
else throw new Exception("Key not found in config")
}
You will have to import the conversions into the current scope when you use getConfig.
val recModel = new Object with hasConfig
import recModel._
recModel.loadConfigs(prop)
val value: Boolean = recModel.getConfig("framework.booleanvalue")
Implicit parameters
A better way would be to keep your current API, but then you will have to introduce an implicit parameter because the implementation of getConfigAs needs access to the conversion.
def getConfigAs[T](key:String)(implicit conv: String => T): T = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
value
}
else throw new Exception("Key not found in config")
}
You will still need to import the necessary conversions at the use site though.
val recModel = new Object with hasConfig
import recModel._
recModel.loadConfigs(prop)
val value = recModel.getConfigAs[Boolean]("framework.booleanvalue")
Typeclasses
A way to avoid having to import your conversions (and possibly implicitly converting all kinds of Strings by accident) is to introduce a new type to encode your conversions. Then you can implement the conversions in its companion object, where implicit search can find them without importing them.
trait Converter[To]{
def convert(s: String): To
}
object Converter {
implicit val string2array: Converter[Array[String]] = new Converter[Array[String]] {
def convert(s: String): Array[String] =
LoadedProperties.getList(s)
}
implicit val string2boolean: Converter[Boolean] = new Converter[Boolean] {
def convert(s: String): Boolean =
s.toLowerCase() match {
case "true" => true
case "false" => false
}
}
}
Then you can change your getConfigAs method.
def getConfigAs[T](key:String)(implicit conv: Converter[T]): T = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
conv.convert(value)
}
else throw new Exception("Key not found in config")
}
And use it.
val recModel = new Object with hasConfig
recModel.loadConfigs(prop)
val value = recModel.getConfigAs[Boolean]("framework.booleanvalue")
You might also want to take a look over here.
Implicit conversions should be defined in scope, for example in enclosing object or imported into the current scope. In your case they should be defined in scope of the hasConfigTest class.
http://docs.scala-lang.org/tutorials/FAQ/finding-implicits
Here's a simple reproducible example:
object m {
implicit def string2boolean(s: String): Boolean = {
s.toLowerCase() match {
case "true" => true
case "false" => false
}
} //> string2boolean: (s: String)Boolean
println(false || "true") //> true
println(false || "false") //> false
}
I think what you are trying to say, is something like this:
import java.util.Properties
object LoadedProperties {
def getList(s: String): Array[String] = Array.empty
}
object hasConfig {
sealed trait ConfigReader[T] {
def read(conf: String): T
}
implicit object BooleanConfigReader extends ConfigReader[Boolean] {
override def read(conf: String): Boolean = conf.toLowerCase() match {
case "true" => true
case "false" => false
}
}
implicit object ArrayConfigReader extends ConfigReader[Array[String]] {
override def read(s: String): Array[String] = {
LoadedProperties.getList(s)
}
}
var config: Properties = new Properties()
def getConfigs: Properties = config
def loadConfigs(prop: Properties): Properties = {
config = prop
config
}
def getConfigAs[T](key: String)(implicit reader: ConfigReader[T]): T = {
val prop = config.getProperty(key)
if (prop == null)
throw new Exception("Key not found in config")
reader.read(prop)
}
}
val props = new Properties()
props.setProperty("a", "false")
props.setProperty("b", "some")
hasConfig.loadConfigs(props)
hasConfig.getConfigAs[Boolean]("a")
hasConfig.getConfigAs[Array[String]]("a")
Can't I use a generic on the unapply method of an extractor along with an implicit "converter" to support a pattern match specific to the parameterised type?
I'd like to do this (Note the use of [T] on the unapply line),
trait StringDecoder[A] {
def fromString(string: String): Option[A]
}
object ExampleExtractor {
def unapply[T](a: String)(implicit evidence: StringDecoder[T]): Option[T] = {
evidence.fromString(a)
}
}
object Example extends App {
implicit val stringDecoder = new StringDecoder[String] {
def fromString(string: String): Option[String] = Some(string)
}
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor[String](x) => x // <- type hint barfs
}
println(result)
}
But I get the following compilation error
Error: (25, 10) not found: type ExampleExtractor
case ExampleExtractor[String] (x) => x
^
It works fine if I have only one implicit val in scope and drop the type hint (see below), but that defeats the object.
object Example extends App {
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor(x) => x
}
println(result)
}
A variant of your typed string decoder looks promising:
trait StringDecoder[A] {
def fromString(s: String): Option[A]
}
class ExampleExtractor[T](ev: StringDecoder[T]) {
def unapply(s: String) = ev.fromString(s)
}
object ExampleExtractor {
def apply[A](implicit ev: StringDecoder[A]) = new ExampleExtractor(ev)
}
then
implicit val intDecoder = new StringDecoder[Int] {
def fromString(s: String) = scala.util.Try {
Integer.parseInt(s)
}.toOption
}
val asInt = ExampleExtractor[Int]
val asInt(Nb) = "1111"
seems to produce what you're asking for. One problem remains: it seems that trying to
val ExampleExtractor[Int](nB) = "1111"
results in a compiler crash (at least inside my 2.10.3 SBT Scala console).
I'm trying to figure out a way to call a Java API from Scala. Basically, there's a ContentValues object that has a couple of methods like getAsString, getAsLong, each having their own different return type.
Can I wrap ContentValues in another object so that I can add a get[T] method that calls the correct getAsXXX method depending on T?
What I tried (didn't work, complains about ambiguous implicit resolution) :
object SContentValuesConversions {
case class SContentGetter[ScalaType](val check: String => Boolean, val getter: String => ScalaType) {
def getTyped(key: String): Option[ScalaType] = {
if (!check(key)) None
else Some(getter(key))
}
}
implicit def SIntContentValues(cv: ContentValues) =
SContentGetter((cv containsKey _), (cv getAsInteger _))
implicit def SLongContentValues(cv: ContentValues) =
SContentGetter((cv containsKey _), (cv getAsLong _))
implicit def SStringContentValues(cv: ContentValues) =
SContentGetter((cv containsKey _), (cv getAsString _))
}
You can use the same technique as the CanBuildFrom trait of collections.
We first create a Getter case class
case class Getter[T](getter: (ContentValues, String) => T) {
def getOpt(contentValues: ContentValues, key: String): Option[T] =
if (contentValues containsKey key) Some(getter(contentValues, key))
else None
}
This allows us to create a ContentValues wrapper that has the desired method.
implicit class ContentValuesWrapper(val c: ContentValues) extends AnyVal {
def getAsOpt[T](key: String)(implicit getter: Getter[T]) =
getter.getOpt(c, key)
}
Now, in order to call the getAsOpt method on ContentValues we need to provide implicit instances of Getter for the correct types.
object Getter {
implicit val intGetter = Getter(_ getAsInteger _)
implicit val longGetter = Getter(_ getAsLong _)
implicit val stringGetter = Getter(_ getAsString _)
}
Now you can use the getAsOpt method on a ContentValues instance.
// fake version of ContentValues
val c =
new ContentValues {
val m = Map("a" -> "1", "b" -> "2", "c" -> "3")
def getAsInteger(k: String): Int = getAsString(k).toInt
def getAsLong(k: String): Long = getAsString(k).toLong
def getAsString(k: String): String = m(k)
def containsKey(k: String): Boolean = m contains k
}
c.getAsOpt[Int]("a") //Option[Int] = Some(1)
c.getAsOpt[Long]("b") //Option[Long] = Some(2)
c.getAsOpt[String]("c") //Option[String] = Some(3)
c.getAsOpt[Int]("d") //Option[Int] = None
c.getAsOpt[Long]("e") //Option[Long] = None
c.getAsOpt[String]("f") //Option[String] = None