Получить имя атрибута класса или функции класса в виде строки

Скажем, у меня есть следующий класс:

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

Можно ли получить строковое представление «firstName» безопасным способом, путем отражения или чего-то еще? Или строковое представление сгенерированной функции "getFirstName"?

Было бы неплохо, если бы это как-то выглядело так:

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

ИЗМЕНИТЬ

Хорошо, нужно больше пояснений ;)

Скажем, я хочу построить SQL-запрос следующим образом:

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

Поэтому я хочу сделать его максимально безопасным для типов и написать что-то вроде этого:

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

Таким образом, если в будущем появится возможность реорганизовать код Scala, я мог бы изменить имя атрибута Person, не нарушая свой код.


person Wolkenarchitekt    schedule 10.06.2010    source источник
comment
Ты нашел способ обойтись без Скуэрила?   -  person GermainGum    schedule 15.06.2015
comment
@GermainGum Нет. Это не отвечает на первоначальный вопрос, но ЕСЛИ мне все еще придется использовать Scala в настоящее время, я бы просто использовал одну из более новых библиотек Scala SQL, например slick slick.typesafe.com, чтобы справиться с этим.   -  person Wolkenarchitekt    schedule 16.06.2015


Ответы (2)


Плохая новость заключается в том, что Scala на самом деле не имеет возможности ссылаться на членов таким образом. Вы можете получить "ссылку на метод" следующим образом:

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

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

scala> methodRef()                                
res1: String = i

Но это не дает вам отражающего материала, который вы хотите. Хорошей новостью является то, что есть несколько библиотек, которые предоставляют такой безопасный тип JDBC. Вот ваш код, использующий 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"!
    }
  } 
}
}

Как это круто? Это не скомпилируется, если, например, вы попытаетесь сравнить возраст со строкой. Конечно, он также не будет компилироваться, если вы используете p.ssn или какое-то другое неизвестное поле.

person Adam Rabung    schedule 11.06.2010
comment
Спасибо! Мой jdbc-пример был просто примером;) да, есть несколько хороших библиотек, чтобы делать такие вещи... но мне больше любопытно и интересно, возможно ли вообще делать то, что я просил. Может быть, мне следует изучить источник Squeryl... Кстати, в настоящее время я использую JPA Criteria Query для своих запросов, который также является типобезопасным, но довольно громоздким для небольших приложений... - person Wolkenarchitekt; 11.06.2010
comment
Для этого Squeryl использует cglib и asm. - person Adam Rabung; 11.06.2010
comment
Хорошо, приятно знать. Так что это возможно только с JVM/bytecode-magic и -frameworks. Хм, я надеялся, что Scala будет поддерживать подобные вещи на уровне языка. - person Wolkenarchitekt; 11.06.2010

Я предполагаю, что вы не знаете имена методов и атрибутов в классе, который хотите проверить (иначе зачем вам получать их имена, а не просто вводить их?). В этом примере показано, как получить имена всех методов класса java.lang.String.

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...
person Abhinav Sarkar    schedule 10.06.2010
comment
Спасибо! Но мне нужна строка одного КОНКРЕТНОГО поля, а не ВСЕ поля! Как и в моем примере: 'p.getAttributeName(p.firstName).toString' Я также был бы рад услышать, что это НЕ возможно, тогда я могу прекратить поиск;) - person Wolkenarchitekt; 11.06.2010
comment
Может быть, я упускаю смысл, но если вы уже вводите имя поля в p.getAttributeName(p.firstName).toString, то почему бы просто не указать его как firstName? - person Abhinav Sarkar; 11.06.2010
comment
Хорошо, я добавил некоторые пояснения к моему сообщению, чтобы прояснить мою проблему. - person Wolkenarchitekt; 11.06.2010