Q
動態創建案例類
1
A
回答
0
所以基本上你需要一個關於你的案例類的運行時信息?我猜你應該使用ClassTag
:
import scala.reflect._
def asCsvReader[T: ClassTag]: T = {
classTag[T].runtimeClass.getDeclaredConstructor(...).newInstance(...)
...
}
這將允許您在運行時實例化您的案例類。
由於您可以計算出CSV列的類型,因此您可以在getDeclaredConstructor
和newInstance
方法中提供相應的類型。
1
如果您正在使用Scala的2.10,您可以使用類在scala.tools.nsc.interpreter
包來完成。請注意,這不再適用於Scala 2.11。我詢問了一個new question,希望我們能得到答案。
在斯卡拉2.10,使用該解釋就可以編譯外部類文件中,幾乎加載它。
的步驟是:
- 圖出你的類的名稱根據約定
- 解析您CSV標題就知道字段名和數據類型
- 生成的情況下,類以上信息寫入磁盤上的文件
- 加載生成的源文件並使用解釋器編譯它們
- 類現在可以使用了。
我建立了一個小的演示,應該幫助。
/**
* Location to store temporary scala source files with generated case classes
*/
val classLocation: String = "/tmp/dynacode"
/**
* Package name to store the case classes
*/
val dynaPackage: String = "com.example.dynacsv"
/**
* Construct this data based on your data model e.g. see data type for Person and Address below.
* Notice the format of header, it can be substituted directly in a case class definition.
*/
val personCsv: String = "PersonData.csv"
val personHeader: String = "title: String, firstName: String, lastName: String, age: Int, height: Int, gender: Int"
val addressCsv: String = "AddressData.csv"
val addressHeader: String = "street1: String, street2: String, city: String, state: String, zipcode: String"
/**
* Utility method to extract class name from CSV file
* @param filename CSV file
* @return Class name extracted from csv file name stripping out ".ext"
*/
def getClassName(filename: String): String = filename.split("\\.")(0)
/**
* Generate a case class and persist to file
* @param file External file to write to
* @param className Class name
* @param header case class parameters
*/
def writeCaseClass(file: File, className: String, header: String): Unit = {
val writer: PrintWriter = new PrintWriter(file)
writer.println("package " + dynaPackage)
writer.println("case class " + className + "(")
writer.println(header)
writer.println(") {}")
writer.flush()
writer.close()
}
/**
* Generate case class and write to file
* @param filename CSV File name (should be named ClassName.csv)
* @param header Case class parameters. Format is comma separated: name: DataType
* @throws IOException if there is problem writing the file
*/
@throws[IOException]
private def generateClass(filename: String, header: String) {
val className: String = getClassName(filename)
val fileDir: String = classLocation + File.separator + dynaPackage.replace('.', File.separatorChar)
new File(fileDir).mkdirs
val classFile: String = fileDir + File.separator + className + ".scala"
val file: File = new File(classFile)
writeCaseClass(file, className, header)
}
/**
* Helper method to search code files in directory
* @param dir Directory to search in
* @return
*/
def recursiveListFiles(dir: File): Array[File] = {
val these = dir.listFiles
these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}
/**
* Compile scala files and keep them loaded in memory
* @param classDir Directory storing the generated scala files
* @throws IOException if there is problem reading the source files
* @return Classloader that contains the compiled external classes
*/
@throws[IOException]
def compileFiles(classDir: String): AbstractFileClassLoader = {
val files = recursiveListFiles(new File(classDir))
.filter(_.getName.endsWith("scala"))
println("Loaded files: \n" + files.mkString("[", ",\n", "]"))
val settings: GenericRunnerSettings = new GenericRunnerSettings(err => println("Interpretor error: " + err))
settings.usejavacp.value = true
val interpreter: IMain = new IMain(settings)
files.foreach(f => {
interpreter.compileSources(new BatchSourceFile(AbstractFile.getFile(f)))
})
interpreter.getInterpreterClassLoader()
}
//Test Address class
def testAddress(classLoader: AbstractFileClassLoader) = {
val addressClass = classLoader.findClass(dynaPackage + "." + getClassName(addressCsv))
val ctor = addressClass.getDeclaredConstructors()(0)
val instance = ctor.newInstance("123 abc str", "apt 1", "Hello world", "HW", "12345")
println("Instantiated class: " + instance.getClass.getCanonicalName)
println(instance.toString)
}
//Test person class
def testPerson(classLoader: AbstractFileClassLoader) = {
val personClass = classLoader.findClass(dynaPackage + "." + getClassName(personCsv))
val ctor = personClass.getDeclaredConstructors()(0)
val instance = ctor.newInstance("Mr", "John", "Doe", 25: java.lang.Integer, 165: java.lang.Integer, 1: java.lang.Integer)
println("Instantiated class: " + instance.getClass.getCanonicalName)
println(instance.toString)
}
//Test generated classes
def testClasses(classLoader: AbstractFileClassLoader) = {
testAddress(classLoader)
testPerson(classLoader)
}
//Main method
def main(args: Array[String]) {
try {
generateClass(personCsv, personHeader)
generateClass(addressCsv, addressHeader)
val classLoader = compileFiles(classLocation)
testClasses(classLoader)
}
catch {
case e: Exception => e.printStackTrace()
}
}
}
輸出:
Loaded files:
[/tmp/dynacode/com/example/dynacsv/AddressData.scala,
/tmp/dynacode/com/example/dynacsv/PersonData.scala]
Instantiated class: com.example.dynacsv.AddressData
AddressData(123 abc str,apt 1,Hello world,HW,12345)
Instantiated class: com.example.dynacsv.PersonData
PersonData(Mr,John,Doe,25,165,1)
2
與dveim提示在this answer,我補充說,無論是在斯卡拉2.10和2.11的作品,並使用Scala的Toolbox第二方案。不幸的是,生成的案例類在默認包中。
使用工具箱
/**
* Helper method to search code files in directory
* @param dir Directory to search in
* @return
*/
def recursiveListFiles(dir: File): Array[File] = {
val these = dir.listFiles
these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}
/**
* Compile scala files and keep them loaded in memory
* @param classDir Directory storing the generated scala files
* @throws IOException if there is problem reading the source files
* @return Map containing Class name -> Compiled Class Reference
*/
@throws[IOException]
def compileFiles(classDir: String): Map[String, Class[_]] = {
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val files = recursiveListFiles(new File(classDir))
.filter(_.getName.endsWith("scala"))
println("Loaded files: \n" + files.mkString("[", ",\n", "]"))
files.map(f => {
val src = Source.fromFile(f).mkString
val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
getClassName(f.getName) -> clazz
}).toMap
}
實例化生成案例類
/**
* Generate a case class and persist to file
* Can't add package to the external class
* @param file External file to write to
* @param className Class name
* @param header case class parameters
*/
def writeCaseClass(file: File, className: String, header: String): Unit = {
val writer: PrintWriter = new PrintWriter(file)
writer.println("case class " + className + "(")
writer.println(header)
writer.println(") {}")
writer.println("\nscala.reflect.classTag[" + className + "].runtimeClass")
writer.flush()
writer.close()
}
編譯外部類和使用編譯的類
編譯的類可以從地圖獲得,並且在需要時使用例如
//Test Address class
def testAddress(map: Map[String, Class[_]]) = {
val addressClass = map("AddressData")
val ctor = addressClass.getDeclaredConstructors()(0)
val instance = ctor.newInstance("123 abc str", "apt 1", "Hello world", "HW", "12345")
//println("Instantiated class: " + instance.getClass.getName)
println(instance.toString)
}
相關問題
- 1. 動態類型實例創建
- 2. 創建類型的實例動態
- 3. 動態創建類
- 4. 動態創建類
- 5. 動態創建類
- 6. 動態創建TableAdapter實例
- 7. 創建動態創建類的實例,並
- 8. WinForms性能案例:動態表單創建+綁定
- 9. 在php中動態創建動態類
- 10. 動態創建動畫片段實例
- 11. 動態創建子類iphone
- 12. Flex/AS3動態創建類
- 13. 動態創建類 - Python
- 14. 動態創建一個類
- 15. Ruby:動態創建新類
- 16. 動態創建子類
- 17. 動態類型創建
- 18. 動態測試案例
- 19. 動態創建網站實例
- 20. Flash Builder。創建動態mxml包實例
- 21. 動態實例創建沒有泛型
- 22. 用類名稱動態創建類
- 23. 如何在python中動態創建一個類的實例?
- 24. 動態創建類的多個實例並引用它
- 25. 在PHP類中動態創建的實例變量
- 26. 動態創建IList類型的新實例
- 27. 動態創建類型爲(self)的實例而不調用__init__?
- 28. 團結不能解決動態創建類的實例
- 29. 如何從動態創建的類獲取對象的實例
- 30. 基於動態項目列表創建類實例
您如何看待您在程序中操作這些案例類而不知道它們是什麼樣的?一旦你回答了這個問題,你就知道你想要使用的結構的形狀。 – Alec
我只會訪問傳遞給它們的變量。例如case class blah(val s:String)我只會訪問s變量。 – kylepotts
在這種情況下,使用不同的庫(或同一庫中的不同方法)可能更容易,它提供CSV記錄的通用表示(例如元組或無形'HList')。 – devkat