Scala泛型实例,无法在泛型类型内引用变量。

huangapple go评论59阅读模式
英文:

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 &lt;: RecDefClass](instance:T)
}

case class Test(id:Long = 1, name:String = &quot;Name of Test&quot;) extends RecDefClass

object Test extends RecDefObj {
  def main(args:Array[String]):Unit = {
    runTest
  }
  def doInstance[Test](instance:Test) = {
    println(&quot;Class of &#39;instance&#39;    -- &quot; + instance.getClass.getName)
    println(&quot;Class of object Test   -- &quot; + this.getClass.getName)
    println(&quot;toString of &#39;instance&#39; -- &quot; + 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 &#39;instance&#39;    -- test.Test
Class of object Test   -- test.Test$
toString of &#39;instance&#39; -- 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 - 这意味着它不再期望是 &lt;: 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 &lt;: RecDefClass](instance:T) vs doInstance[Test](instance:Test). You widened the generic (as if you did def doInstance[Test &lt;: Any](instance:Test)). You did NOT applied Test as type parameter on definition (it's not C++ template specialization).

Original accepted T &lt;: RecDefClass, the implementation uses type parameter Test - which means that it no longer expects it to be &lt;: 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 &lt;: 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 &lt;: Any](instance:T) = {
    println(&quot;Class of &#39;instance&#39;    -- &quot; + instance.getClass.getName)
    // instance doesn&#39;t have to be of instance `Test`
    // so the code below is lying
    println(&quot;Class of object Test   -- &quot; + this.getClass.getName)
    println(&quot;toString of &#39;instance&#39; -- &quot; + 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
  }
}

huangapple
  • 本文由 发表于 2023年5月15日 02:49:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76249168.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定