如何让trait的实现者定义上下文参数类型
实现自定义上下文类型的Trait设计
问题描述
希望在库中定义一个trait,其中某个方法的上下文参数类型由库的调用方(即trait的实现者)指定。现有示例代码因上下文类型不匹配导致编译失败,需要找到可行的实现方式。
原示例代码
//////////////////////// // 库代码 //////////////////////// trait Animal: // 目标:传递给`make_sound`的上下文类型由实现者定义 def make_sound()(using ctx: ???): Unit def do_something(animal: Animal) = { animal.make_sound() } //////////////////////// // 用户代码 //////////////////////// trait CatMakeSoundContext: def get_claw_length(): Int class Cat extends Animal: // 编译失败,因为与`Animal`中的签名不一致 override def make_sound()(using ctx: CatMakeSoundContext) = { if ctx.get_claw_length() > 2 { println("scratch!") } else { println("meow") } } class MyCatMakeSoundContext extends CatMakeSoundContext { override def get_claw_length(): Int = 2 } def call_example() = { val cat: Cat = ...; val claw_context = MyCatMakeSoundContext() // 通过某种方式用`given`子句设置`claw_context` // 供`animal.make_sound()`使用 // 调用`do_something()`,最终会将`claw_context` // 作为`make_sound()`中隐式参数`ctx`的值 do_something(cat) }
解决方案:使用泛型Trait
核心思路是给Animal trait添加泛型参数,用来表示该动物类型所需的上下文类型。这样每个实现类都可以指定自己的上下文类型,同时保证方法签名的一致性。
修改后的库代码
//////////////////////// // 库代码 //////////////////////// // 新增泛型参数C,表示该Animal所需的上下文类型 trait Animal[C]: def make_sound()(using ctx: C): Unit // 将do_something改为泛型方法,接收特定上下文类型的Animal def do_something[C](animal: Animal[C])(using ctx: C) = { animal.make_sound() }
修改后的用户代码
//////////////////////// // 用户代码 //////////////////////// trait CatMakeSoundContext: def get_claw_length(): Int // Cat实现Animal时指定上下文类型为CatMakeSoundContext class Cat extends Animal[CatMakeSoundContext]: override def make_sound()(using ctx: CatMakeSoundContext) = { if ctx.get_claw_length() > 2 { println("scratch!") } else { println("meow") } } class MyCatMakeSoundContext extends CatMakeSoundContext { override def get_claw_length(): Int = 2 } def call_example() = { val cat = Cat() given claw_context: CatMakeSoundContext = MyCatMakeSoundContext() // 此时调用do_something会自动传入给定的上下文 do_something(cat) }
补充说明
如果需要处理多种不同上下文类型的Animal,可以使用Scala的存在类型定义通用类型:
type AnyAnimal = Animal[_]
但这种情况下无法直接调用make_sound,因为编译器无法确定所需的上下文类型。因此更推荐保持泛型设计,让调用方明确传递上下文,或者根据业务场景引入更抽象的上下文父类型,让不同实现类共享基础上下文能力。
内容的提问来源于stack exchange,提问作者plafer
相关产品推荐
相关产品推荐

