3

Say i have the following class:

class Person {
  @BeanProperty
  var firstName: String = _
}

Is it possible to get the String representation of "firstName" in a type-safe way, by reflection or something? Or the String representation of the generated "getFirstName"-Function?

It would be nice if it would somehow look like:

val p = new Person
p.getFunction(p.getFirstName).toString // "getFirstName"
p.getAttribute(p.firstName).toString   // "firstName"

EDIT

Ok, more explanation is needed ;)

Say i want to build a SQL query like this:

val jpql = "select p from Person p where p.age > 20";

So i want to make it as typesafe as possible and write something like this:

val jpql = "select p from " + classOf[Person].getName + " where p." + 
  Person.getAttName(p.age) + " > 20";

In this way, if it's possible to refactor Scala code in the future, i could change the attribute name of Person without breaking my code.

2
  • Did you find a way to do without Squeryl? Commented Jun 15, 2015 at 12:26
  • @GermainGum No. This does not answer the original question, but IF I would still have to use Scala nowadays I would just use one of the newer Scala SQL libraries like slick slick.typesafe.com to handle this. Commented Jun 16, 2015 at 14:11

2 Answers 2

2

The bad news is Scala doesn't really have the ability to reference members like that. You can get a "method reference" like this:

scala> class Person(val firstName:String)         
defined class Person

scala> val methodRef = new Person("i").firstName _
methodRef: () => String = <function0>

scala> methodRef()                                
res1: String = i

But it doesn't give you the reflective stuff that you want. The good news is there are a couple of libraries out there that give you this type of type-safe JDBC. Here's your code using Squeryl:

import org.squeryl.Schema
import org.squeryl.Session
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.adapters.H2Adapter
import org.squeryl.SessionFactory

object Sample {
case class Person(val firstName:String, val age:Int)

object AppSchema extends Schema {
  val people = table[Person]("People")
}

def main(args:Array[String]) { 
  import AppSchema.people
  Class.forName("org.h2.Driver")
  SessionFactory.concreteFactory = Some(()=> Session.create(java.sql.DriverManager.getConnection("jdbc:h2:~/temp/db", "sa", ""), new H2Adapter))

  transaction {
    AppSchema.create
    people.insert(new Person("ifischer", 92)) 
    people.insert(new Person("baby", 2)) 
    for (olderPerson <- from(people)(p=> where(p.age gt 20) select(p))) {
      println(olderPerson) //wont return "baby"!
    }
  } 
}
}

How cool is that? This won't compile if, for example, you try to compare age to a String. Of course it also won't compile if you use p.ssn or some other unknown field.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! My jdbc-example was just an example ;) yes there are some good libraries out there to do things like that... but i'm more kind of curious and interested if it is possible in general to do what i asked. Maybe i should look into the source of Squeryl... BTW Currently i'm using JPA Criteria Query for my queries, which is also typesafe, but kind of cumbersome for small apps...
Squeryl uses cglib and asm to achieve this.
Ok, good to know. So this only possible with JVM/bytecode-magic and -frameworks. Hmm i hoped that Scala would support stuff like this on language level.
2

I am assuming that you don't know the method and attribute names in the class you want to inspect (otherwise why would you want to get their names rather than just typing them in?). This example shows how to get names of all the methods on the java.lang.String class.

scala> classOf[String].getMethods.map(_.getName)
res4: Array[java.lang.String] = Array(equals, hashCode, toString, charAt, compareTo, 
compareToIgnoreCase, concat, endsWith, equalsIgnoreCase, getBytes, getBytes, getBytes, 
getChars, indexOf, indexOf, indexOf, indexOf, intern, lastIndexOf, lastIndexOf, 
lastIndexOf, lastIndexOf, length, regionMatches, regionMatches, replace, startsWith, 
startsWith, substring, substring, toCharArray, toL...

3 Comments

Thanks! But i need the String of one SPECIFIC field, not ALL of the fields! Like in my example: 'p.getAttributeName(p.firstName).toString' I would also be happy to hear that its NOT possible, then i can stop searching ;)
Maybe I am missing the point but if you are already typing the name of the field in p.getAttributeName(p.firstName).toString then why not just put it as firstName?
Ok, i added some explanation to my post to clearify my problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.