質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

Q&A

1回答

2590閲覧

[Scala] 型パラメータ(ジェネリクス)に指定するCaseクラスを継承元でインスタンス化する方法

kouzouman

総合スコア18

Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

0グッド

0クリップ

投稿2016/10/20 13:18

編集2016/10/22 02:21

継承元クラスでSelectのSQLを実行して、継承先クラスで戻り値を受け取る構造を作りたいと思っています。
想定している大体の構造は次のとおりです。

※この構造(型パラメータ)でなくても、
継承元で同一の基底クラスを持つCaseクラスをインスタンス化できるのであれば、
その方法が知りたいです。

scala

1// dbbase.scala 2 3/** 4 * 各行の基底クラス 5 */ 6public class RowBase { 7} 8 9/** 10 * DBに関するモデルベース 11 */ 12class DbAccessBase[T <: RowBase] { 13 14 /** 15 * Select文を実行する 16 * @param sql SQLオブジェクト 17 */ 18 def execSelect(sql: SQL[Nothing, NoExtractor]): List[T] = { 19 20 // この部分を動作できるようにさせたい 21 sql.map(T(_)).list.apply() 22 } 23} 24

scala

1// user.scala 2 3/** 4 * Userの行データのCaseClass 5 * 6 * @param id ID 7 * @param email メールアドレス 8 */ 9case class User(id : Int, email : String) extends RowBase{} 10 11/** 12 * DBからPeopleテーブルの内容を取得するためのコンパニオンオブジェクト 13 */ 14object User extends SQLSyntaxSupport[User] { 15 16 /** 17 * Apply caseu クラスへの置換用 18 * @param rs 19 * @return インスタンス 20 */ 21 def apply(rs: WrappedResultSet): PeopleRow = new PeopleRow(id = rs.long("id"), email = rs.string("email")) 22} 23 24/** 25 * Userテーブルのアクセサ 26 */ 27class PeopleData[User] extends DbAccessBase{ 28 29 /** 30 * ユーザ情報を取得 31 * @param id ID 32 * @return ユーザ情報 33 */ 34 def getUser(id:Int) : List[User] = { 35 36 // これは実行できるが、継承元クラスでしたい 37 // sql"select id, email from user where id = ${id}".map(User(_)).list.apply() 38 39 // このように継承元で実行したい 40 this.execSelect(sql"select id, email from user where id = ${id}") 41 } 42}

DbAccessBaseのexecSelectを実行できるようにしたいと思っています。
仕様的に、型パラメータはインスタンス化できないので、コンパイルも通らない状態です。

次のようにリフレクションを使う方法も考えましたが、
インスタンス化するときに引数を指定できないため、断念しました。

scala

1 2 3/** 4 * DBに関するモデルベース 5 */ 6class DbAccessBase[T <: RowBase] { 7 /** 8 * テンプレートをインスタンス化する 9 * @param sql SQLオブジェクト 10 */ 11 def createTypeInstance(arg: WrappedResultSet): T = { 12 val selfClass = this.getClass 13 val typeVal = selfClass.getGenericSuperclass 14 val pt = typeVal.asInstanceOf[ParameterizedType] 15 val atArgs = pt.getActualTypeArguments 16 val entityClass = atArgs(0).asInstanceOf[Class[_]] 17 entityClass.newInstance(/*ここに引数 arg を指定したい*/).asInstanceOf[T] 18 } 19 20 /** 21 * Select文を実行する 22 * @param sql SQLオブジェクト 23 */ 24 def execSelect(sql: SQL[Nothing, NoExtractor]): List[T] = { 25 sql.map(this.createTypeInstance(_)).list.apply() 26 } 27}

本来型パラメータをインスタンス化するという発想そのものが言語仕様から見たらご法度なのかもしれませんが、
いくつかのSelect結果のキャッシュ化など、共通処理を基底クラスで行いたいためこういう構造で実装したいと考えています。

何か良いアイディアはありませんでしょうか。

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ジェネリクスがJavaとどう違うのだろうということを調べたときManifestやTypeTagを使った方法の記事を読んだことがあります。どういった制約があるか詳しくないですがそれらの利用については検討されたのでしょうか?

例えば下記の記事はManifestでの実装をTypeTagでの実装にどう変えるというような内容と思いますが、コンストラクターをオーバーロードしない前提ならこの記事のManifestを使ったコードがそのまま利用できるような気がしました。(オーバーロードしてあってもその配慮さえあればいける気もします)

StackOverflow: How to create an instance of type T at runtime with TypeTags

自分が調べた当時は古いバージョンのScalaを使っていたせいかTypeTagサポート途中(?)だったような記憶があります。今どういう仕様なのかはおさえてません・・・

投稿2016/10/29 04:19

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問