英文:
scala generic instance, cannot reference variable within the generic type
问题
I'm here to provide you with the translated content. Here is the translated text from the code and the question:
卡在这个问题上。可以使用一种变通方法,但不想失去类型安全性。示例代码:
package test
特质 RecDefClass
特质 RecDefObj {
def runTest = doInstance(Test())
def doInstance[T <: RecDefClass](instance:T)
}
案例类 Test(id:Long = 1, name:String = "Test名称") 扩展 RecDefClass
对象 Test 扩展 RecDefObj {
def main(args:Array[String]):Unit = {
runTest
}
def doInstance[Test](instance:Test) = {
println("实例的类 -- " + instance.getClass.getName)
println("Test对象的类 -- " + this.getClass.getName)
println("实例的toString -- " + instance.toString)
val fixed = instance.asInstanceOf[Test]
// instance.id
// fixed.id
}
}
代码中的第21行 -- instance.id -- 生成编译错误。甚至第22行 -- fixed.id -- 生成编译错误(即使 'fixed' 被强制转换为类型 Test)。
将这些行注释掉后,代码执行并产生以下输出,与预期完全一致:
实例的类 -- test.Test
Test对象的类 -- test.Test$
实例的toString -- Test(1,Test名称)
有什么想法、建议吗?
谢谢
Please note that I have translated the code and the question, as requested.
英文:
Stuck on this problem. Can use a workaround, but hate to lose the type-safety. Sample code:
package test
trait RecDefClass
trait RecDefObj {
def runTest = doInstance(Test())
def doInstance[T <: RecDefClass](instance:T)
}
case class Test(id:Long = 1, name:String = "Name of Test") extends RecDefClass
object Test extends RecDefObj {
def main(args:Array[String]):Unit = {
runTest
}
def doInstance[Test](instance:Test) = {
println("Class of 'instance' -- " + instance.getClass.getName)
println("Class of object Test -- " + this.getClass.getName)
println("toString of 'instance' -- " + instance.toString)
val fixed = instance.asInstanceOf[Test]
// instance.id
// fixed.id
}
}
The code on line 21 -- instance.id -- generates a compile error. Even line 22 -- fixed.id -- generates a compile error (even though 'fixed' was coerced to be of type Test).
With these lines commented out, the code executes and produces this:
Class of 'instance' -- test.Test
Class of object Test -- test.Test$
toString of 'instance' -- Test(1,Name of Test)
exactly as expected.
Any thoughts, suggestions?
Thanks
答案1
得分: 3
def doInstance[T <: RecDefClass](instance:T)
与 doInstance[Test](instance:Test)
。你扩大了泛型(就好像你做了 def doInstance[Test <: Any](instance:Test)
)。你没有在定义上应用 Test
作为类型参数(这不是C++模板特化)。
原本接受 T <: RecDefClass
,但实现中使用了类型参数 Test
- 这意味着它不再期望是 <: RecDefClass
,它可以是你传递的任何类型,只是这次你将它命名为 Test
。
这个 [Test]
类型参数遮蔽了上面的 Test
类型,意味着这个 Test
不再是 case case
而是 T <: Any
。这就是为什么编译器报错的原因。
换句话说:
当你 定义 def something[Test](...)
时,你创建了一个新的类型 Test
,只在 something
内部可见。这个 Test
会遮蔽其他具有相同名称的类型。
当你 调用 something[Test]
时,你告诉编译器传入的对象将使用 Test
类型(在外部已知)替代在参数和返回值中调用的任何类型参数。
因此,你的代码等同于
object Test extends RecDefObj {
def main(args:Array[String]):Unit = {
runTest
}
def doInstance[T <: Any](instance:T) = {
println("Class of 'instance' -- " + instance.getClass.getName)
// instance 不必是 'Test' 的实例
// 所以下面的代码是错误的
println("Class of object Test -- " + this.getClass.getName)
println("toString of 'instance' -- " + instance.toString)
// [Test] 内部遮蔽了外部的 case class Test
// 所以这实际上是你得到的东西
val fixed = instance.asInstanceOf[T]
// instance.id
// fixed.id
}
}
英文:
def doInstance[T <: RecDefClass](instance:T)
vs doInstance[Test](instance:Test)
. You widened the generic (as if you did def doInstance[Test <: Any](instance:Test)
). You did NOT applied Test
as type parameter on definition (it's not C++ template specialization).
Original accepted T <: RecDefClass
, the implementation uses type parameter Test
- which means that it no longer expects it to be <: RecDefClass
, it can be whatever type you pass inside, just that this time you named it Test
.
This [Test]
type parameter overshadows type Test
from above, meaning that this Test
is NOT the case case
but some T <: Any
. That's why the compiler complains.
In other words:
When you are defining def something[Test](...)
you are creating a NEW type Test
seen only inside something
. This Test
would overshade other types having the same name.
When you are calling something[Test]
you are telling compiler that object coming in will use Test
type (as known outside) in place of whatever type parameter was called both in arguments and in returned value.
Your code is thus equal to
object Test extends RecDefObj {
def main(args:Array[String]):Unit = {
runTest
}
def doInstance[T <: Any](instance:T) = {
println("Class of 'instance' -- " + instance.getClass.getName)
// instance doesn't have to be of instance `Test`
// so the code below is lying
println("Class of object Test -- " + this.getClass.getName)
println("toString of 'instance' -- " + instance.toString)
// [Test] inside overshaded case class Test outside
// so this is what you actually got
val fixed = instance.asInstanceOf[T]
// instance.id
// fixed.id
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论