■ はじめに
https://dk521123.hatenablog.com/entry/2023/03/16/012034
で YAMLファイルを扱った。ここで作成した
「例2:独自クラスにマッピングする」「例3:別YAML&リファクタリング」
を、どのクラスでも使えるようにしたい。
そこで、その第一歩として、
今回は、Scala の Generics(ジェネリック/総称型)について学び
最後に曲がりなりにも上記の実装を実現する。
目次
【1】ジェネリック
【2】構文
1)関数
2)クラス
【3】ジェネリックの制限方法
1)境界
2)変位アノテーション
【4】サンプル
例1:ジェネリッククラス
例2:YAMLファイルを汎用的に独自クラスをマッピングする
* ジェネリック(Generic) のことを、Scala では
型パラメータ(Type Parameter or Parameterized types)ともいう
【2】構文
1)関数
def 関数名[型パラメータ, ...](引数):戻り値 = {
関数本体
}
2)クラス
class クラス名[A](クラス引数) {
(フィールド定義|メソッド定義)*
}
1)境界
境界種類 |
記法 |
指定可能なクラス |
境界 |
[T] |
T そのものしか指定できない |
上限境界 |
[T<: クラス名] |
Tには指定されたクラス又はそのサブクラスを指定可能 |
下限境界 |
[T>: クラス名] |
Tには指定されたクラス又はそのスーパークラスを指定可能 |
可視境界 |
[T<% クラス名] |
Tには指定されたクラス又はTに暗黙の型変換が可能なクラスを指定可能 |
class Foo[+A]
class Bar[-A]
class Baz[A]
変位種類 |
記法 |
指定可能なクラス |
引数使用可否 |
戻り値使用可否 |
非変 |
[T] |
T に限られる |
YES |
YES |
共変 |
[+T] |
T とそのサブクラスに限られる |
NO |
YES |
反変 |
[-T] |
T とそのスーパークラスに限られる |
YES |
NO |
* Java の配列は、共変
* Scala の配列は、非変
【3】サンプル
https://docs.scala-lang.org/ja/tour/generic-classes.html
class Stack[T] {
private var elements: List[T] = Nil
def push(value: T): Unit =
this.elements = value :: this.elements
def peek: T = this.elements.head
def pop(): T = {
val currentTop = peek
this.elements = this.elements.tail
currentTop
}
}
例2:YAMLファイルを汎用的に独自クラスをマッピングする
https://dk521123.hatenablog.com/entry/2023/03/16/012034
の「例3:別YAML&リファクタリング」をジェネリックで実装。
結構、苦労した、、、
なお、FileのCloseにUsingを使っているが、
AWS Glue の Scalaバージョンが 2.12なので、
代わりにTry-finallyで実装。
Main.scala
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
import scala.beans.BeanProperty
import java.io.FileInputStream
import scala.reflect._
object Main extends App {
val inputPath = raw"C:\xxxx\process_info.yaml"
val processInfo = readYaml[ProcessInfo](inputPath)
println(processInfo)
def readYaml[T: ClassTag](inputPath: String): T = {
val reader = new FileInputStream(inputPath)
try {
val targetClass = classTag[T].runtimeClass.toString()
val className = targetClass.replaceFirst("class ", "")
val inputYaml = new Yaml(new Constructor(className))
return inputYaml.load(reader).asInstanceOf[T]
} finally {
reader.close()
}
}
}
class ProcessInfo {
@BeanProperty var processName = ""
@BeanProperty var schedule = ""
@BeanProperty var emailListToNotify =
new java.util.ArrayList[String]()
@BeanProperty var subProcesses =
new java.util.HashMap[String, java.util.HashMap[String, Any]]()
override def toString: String =
s"processName : $processName , schedule: $schedule, emailListToNotify : $emailListToNotify , subProcesses : $subProcesses "
}
process_info.yaml
processName: processA
schedule: 10:00-12:00
emailListToNotify:
- sample@gmail
- sample@yahoo.com
subProcesses:
sub-processA1:
inputPath: s3://your-bucket/sub-processA1/input/
outputPath: s3://your-bucket/sub-processA1/output/
sub-processA2:
inputPath: s3://your-bucket/sub-processA2/input/
outputPath: s3://your-bucket/sub-processA2/output/
sub-processA3:
inputPath: s3://your-bucket/sub-processA3/input/
outputPath: s3://your-bucket/sub-processA3/output/
参考文献
https://atmarkit.itmedia.co.jp/ait/articles/1208/09/news128.html
関連記事
Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ YAML ~
https://dk521123.hatenablog.com/entry/2023/03/16/012034
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000