我来自 C++ 世界并且是 Scala 的新手,这种行为看起来很不寻常。
class G1[A]( val a : A) {
//val c:A = new A //This gives compile error
def fcn1(b: A): Unit = {
//val aobj = new A // This gives compile error
println(a.getClass.getSimpleName)
println(b.getClass.getSimpleName)
}
}
def fcnWithTP[A](): Unit = {
//val a = new A // This gives compile error
//println(a.getClass.getSimpleName)
}
我无法在函数体或类体的类中使用类型参数创建对象。我只能在函数参数中使用它。
这是什么原因?这是因为类型删除吗?在运行时,函数不知道实际的类型 A 是什么,所以它不能创建该类型的对象?
这样做的一般规则是什么?这是否意味着类型参数根本不能出现在函数体或类定义中?如果它们真的能出现,有哪些例子?
最佳答案
是的,你说得对,这是因为删除——你对 A
一无所知。在运行时,您没有明确声明它是方法签名中的约束。
JVM 上的类型删除只是部分的,所以你可以在 Scala 中做一些可怕的事情,比如请求一个值的类:
scala> List(1, 2, 3).getClass
res0: Class[_ <: List[Int]] = class scala.collection.immutable.$colon$colon
但是,一旦您使用泛型,所有内容都会被删除,因此例如您无法区分以下内容:
scala> List(1, 2, 3).getClass == List("a", "b", "c").getClass
res1: Boolean = true
(如果不清楚,我认为类型删除无疑是一件好事,JVM 上类型删除的唯一问题是它不够完整。)
您可以编写以下内容:
import scala.reflect.{ ClassTag, classTag }
class G1[A: ClassTag](val a: A) {
val c: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
}
并像这样使用它:
scala> val stringG1: G1[String] = new G1("foo")
stringG1: G1[String] = G1@33d71170
scala> stringG1.c
res2: String = ""
但是,这是一个非常糟糕的主意,因为它会在运行时因许多类型参数而崩溃:
scala> class Foo(i: Int)
defined class Foo
scala> val fooG1: G1[Foo] = new G1(new Foo(0))
java.lang.InstantiationException: Foo
at java.lang.Class.newInstance(Class.java:427)
... 43 elided
Caused by: java.lang.NoSuchMethodException: Foo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 43 more
更好的方法是传入构造函数:
class G1[A](val a: A)(empty: () => A) {
val c: A = empty()
}
更好的方法是使用类型类:
trait Empty[A] {
def default: A
}
object Empty {
def instance[A](a: => A): Empty[A] = new Empty[A] {
def default: A = a
}
implicit val stringEmpty: Empty[String] = instance("")
implicit val fooEmpty: Empty[Foo] = instance(new Foo(0))
}
class G1[A: Empty](val a: A) {
val c: A = implicitly[Empty[A]].default
}
进而:
scala> val fooG1: G1[Foo] = new G1(new Foo(10101))
fooG1: G1[Foo] = G1@5a34b5bc
scala> fooG1.c
res0: Foo = Foo@571ccdd0
这里我们指的是
A
在G1
的定义中,但我们只引用我们已确认保留或在编译时可用的属性和操作。
关于scala - 类型参数不能在 Scala 的函数体中引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36388265/