Scala继承Java带protected字段类的构造与访问问题
Scala访问Java库protected成员的解决方案
问题场景
Scala项目依赖不可修改的Java库,需要继承BarFoo类、重写init方法并修改父类Foo的protectedBaz字段,但遇到protected可见性限制。
Java库代码:
package com.a; class Foo { protected Baz protectedBaz; Foo(Int i) { ... } Foo(String s) { ... } protected void init() { ... } } package com.b; class BarFoo extends Foo { BarFoo(Int i) { ... } BarFoo(String s) { ... } }
要实现的核心逻辑:
override def init(): Unit = { super.init() // 保留原有初始化逻辑 this.protectedBaz.setA(..) // 修改protected字段 }
最初用trait解决多构造方法问题时触发报错:
object CustomBarFoo { def apply(i: Int) = new BarFoo(i) with CustomBarFoo def apply(s: String) = new BarFoo(s) with CustomBarFoo } trait CustomBarFoo extends BarFoo { override def init(): Unit = { super.init() this.protectedBaz.setA(..) // 报错:Implementation restriction: trait CustomBarFoo accesses protected variable protectedBaz inside a concrete trait method. } }
陷入两难:直接继承BarFoo要处理多构造方法,重定义protected成员可见性又需要子类。
解决方案
通过中间子类+ trait的两层结构解决:用中间子类重定义protected成员的访问权限,再用trait实现业务逻辑。
1. 创建中间子类重定义protected成员
写一个Scala子类继承BarFoo,把protectedBaz包装成同包可访问的方法:
package com.b // 处理整数参数构造器 class BarFooProxy(i: Int) extends BarFoo(i) { // 重定义protected成员,让同包trait能访问 protected[this] def baz: Baz = protectedBaz } // 为字符串参数构造器提供重载 object BarFooProxy { def apply(s: String): BarFooProxy = new BarFooProxy(s) }
2. 在trait中实现业务逻辑
现在trait继承中间子类,通过包装后的baz方法访问目标字段:
package com.b trait CustomBarFoo extends BarFooProxy { override def init(): Unit = { super.init() // 执行原有初始化逻辑 baz.setA(..) // 正常访问无权限问题 } }
3. 封装实例创建入口
用伴生对象统一实例创建方式:
package com.b object CustomBarFoo { def apply(i: Int): BarFooProxy with CustomBarFoo = new BarFooProxy(i) with CustomBarFoo def apply(s: String): BarFooProxy with CustomBarFoo = BarFooProxy(s) with CustomBarFoo }
核心原理
Scala对protected的访问规则比Java更严格:trait的具体方法无法直接访问父类的protected成员,除非该成员被重定义为可访问。中间子类把protectedBaz包装成可被trait访问的方法,完美绕过这个限制,同时解决了多构造方法的问题。
内容的提问来源于stack exchange,提问作者Hedrack
相关产品推荐
相关产品推荐

