sbt run
--- Testing MyService ---
Service Type: MyService
Companion Info: Companion for MyService
Companion Port: 8080
Base Logic: Executing common logic for MyService
--- Testing AnotherService ---
Service Type: AnotherService
Companion Info: Companion for AnotherService (Config: DefaultConfig123)
Companion Port: 9090
Base Logic: Executing common logic for AnotherService
Created
April 18, 2025 08:06
-
-
Save y-yoshinoya/4db5382cac01a9c5cb28ecab52b58739 to your computer and use it in GitHub Desktop.
Get companion object sample (Scala3)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ThisBuild / scalaVersion := "3.6.4" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package extensions | |
import scala.quoted.* | |
trait CompanionProvider[T]: | |
type CompanionType | |
def companion: CompanionType | |
object CompanionProvider: | |
private def deriveImpl[T: Type](using q: Quotes): Expr[CompanionProvider[T]] = | |
import q.reflect.* | |
val tpe = TypeRepr.of[T] | |
val symbol = tpe.typeSymbol | |
if !symbol.isClassDef then | |
report.errorAndAbort(s"Cannot derive CompanionProvider for non-class type ${tpe.show}") | |
val companionSymbol = symbol.companionModule | |
if !companionSymbol.flags.is(Flags.Module) then | |
report.errorAndAbort(s"Cannot find companion object for ${tpe.show}") | |
val companionTypeRepr = Ref(companionSymbol).tpe // コンパニオンの型 (シングルトン型) | |
// companionTypeRepr (例: MyService.type) を型パラメータ C として取得 | |
companionTypeRepr.asType match | |
case '[c] => | |
// CompanionProvider[T] { type CompanionType = c; def companion = ??? } を実装する Expr を構築 | |
'{ | |
new CompanionProvider[T]: | |
type CompanionType = c | |
// ${ Ref(companionSymbol).asExprOf[c] } でコンパニオンオブジェクトへの参照を埋め込む | |
def companion: CompanionType = ${ Ref(companionSymbol).asExprOf[c] } | |
} | |
case _ => | |
report.errorAndAbort(s"Could not resolve companion type for ${tpe.show}") | |
inline given derived[T]: CompanionProvider[T] = ${ deriveImpl[T] } | |
end CompanionProvider | |
trait CompanionInfoProvider { | |
def getServiceInfo(): String | |
def defaultPort: Int | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import extensions._ | |
// BaseService トレイト (CompanionProvider を要求するメソッドを持つ) | |
trait BaseService { self => | |
def serviceType: String | |
def commonBaseLogic(): String = s"Executing common logic for $serviceType" | |
// --- ★★★ 型クラスを使用 ★★★ --- | |
// このメソッドは、呼び出し元の型 T (self の型) に対する | |
// CompanionProvider[T] の given インスタンスを必要とする | |
// (コンパイラが CompanionProvider.derived で自動生成してくれる) | |
def printCompanionAndBaseInfo()(using cp: CompanionProvider[self.type]): Unit = { | |
println(s"Service Type: $serviceType") | |
// 型クラス経由でコンパニオンを取得 | |
val comp = cp.companion | |
// comp の型は cp.CompanionType (例: MyService.type) なので、 | |
// CompanionInfoProvider へのキャスト/マッチは依然として必要 | |
comp match { | |
case cip: CompanionInfoProvider => | |
println(s"Companion Info: ${cip.getServiceInfo()}") | |
println(s"Companion Port: ${cip.defaultPort}") | |
case _ => | |
println(s"Companion object for $serviceType does not provide CompanionInfoProvider.") | |
} | |
println(s"Base Logic: ${commonBaseLogic()}") | |
} | |
} | |
case class MyService(name: String) extends BaseService { | |
override def serviceType: String = "MyService" | |
} | |
object MyService extends CompanionInfoProvider { | |
override def defaultPort: Int = 8080 | |
override def getServiceInfo(): String = "Companion for MyService" | |
} | |
case class AnotherService(id: Int) extends BaseService { | |
override def serviceType: String = "AnotherService" | |
} | |
object AnotherService extends CompanionInfoProvider { | |
override val defaultPort: Int = 9090 | |
override def getServiceInfo(): String = s"Companion for AnotherService (Config: DefaultConfig123)" | |
} | |
@main def runTypeClassExample(): Unit = { | |
println("--- Testing MyService ---") | |
val service1 = MyService("Processor") | |
// printCompanionAndBaseInfo を呼び出すと、コンパイラが裏で | |
// given CompanionProvider[MyService] = CompanionProvider.derived[MyService] を解決する | |
service1.printCompanionAndBaseInfo() | |
println("\n--- Testing AnotherService ---") | |
val service2 = AnotherService(101) | |
// 同様に CompanionProvider[AnotherService] が解決される | |
service2.printCompanionAndBaseInfo() | |
println("\nType class pattern test successful.") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment