我试图创建一个接口,在运行时,我需要一个与泛型类相对应的类对象。
下面的代码显示了我的问题。
package com.example
import java.util.function.Consumer
import scala.reflect.ClassTag
import java.util.Collection
trait DomObj:
def foo(): Unit
trait DomObjA extends DomObj:
def bar(): Unit
/** This is an external, Java API that I cannot change */
trait Store:
def subscribe[T <: DomObj](cls: Class[T], cons: Consumer[Collection[T]]) = ???
/** This is the class I am trying to write */
object Client:
val store: Store = ???
def sub1[T <: DomObj](cons: Consumer[Collection[T]])(using classTag: ClassTag[T]): Unit =
store.subscribe(classTag.runtimeClass, cons)
def sub2[T <: DomObj](cons: Consumer[Collection[T]])(using classTag: ClassTag[T]): Unit =
val cls: Class[T] = classTag.runtimeClass.asSubclass(classOf[DomObj])
store.subscribe(cls, cons)
def sub3[T <: DomObj](cons: Consumer[Collection[T]])(using classTag: ClassTag[T]): Unit =
val cls: Class[T] = classTag.runtimeClass.asSubclass(classOf[T])
store.subscribe(cls, cons)
def subSad[T <: DomObj](cls: Class[T], cons: Consumer[Collection[T]]): Unit =
store.subscribe(cls, cons)
def run(): Unit =
val cons: Consumer[Collection[DomObjA]] = ???
sub1(cons)
sub2(cons)
sub3(cons)
subSad(classOf[DomObjA], cons)
字符串subSad
可以工作,但是我想避免显式指定类。这似乎是合理的,因为编译器知道类,并且应该能够将其作为隐式参数传递。事实上,这似乎是ClassTag
类的目的。
但是,其他所有尝试都无法编译。
- 子1 *
[error] 30 | store.subscribe(classTag.runtimeClass, cons)
[error] | ^^^^^^^^^^^^^^^^^^^^^
[error] | Found: Class[?]
[error] | Required: Class[T]
[error] |
[error] | where: T is a type in method sub1 with bounds <: com.example.DomObj
型
- 次级2 *
[error] 32 | val cls: Class[T] = classTag.runtimeClass.asSubclass(classOf[DomObj])
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | Found: Class[? <: com.example.DomObj]
[error] | Required: Class[T]
[error] |
[error] | where: T is a type in method sub2 with bounds <: com.example.DomObj
型
- 第三次 *
[error] 35 | val cls: Class[T] = classTag.runtimeClass.asSubclass(classOf[T])
[error] | ^
[error] | T is not a class type
[error] |
[error] | where: T is a type in method sub3 with bounds <: com.example.DomObj
型
在Scala 2中,TypeTag
可能完成了我想要的工作,但在Scala 3中似乎不存在。有没有办法让它工作,或者我只需要使用subSad
?
这会编译:
def sub4[T <: DomObj](cons: Consumer[Collection[T]])(using classTag: ClassTag[T]): Unit =
val cls: Class[T] = classTag.runtimeClass.asInstanceOf[Class[T]]
store.subscribe(cls, cons)
型
虽然它使用了asInstanceOf
,这通常是一种代码气味。它只是把类型参数放回我们知道是正确的。
1条答案
按热度按时间fhity93d1#
你提到的强制转换是因为运行时类不一定对应于Scala类型。
ClassTag[T]
中的T
是Scala类型,而Class[U]
中的U
是擦除的Java端类。虽然这两种类型通常一致(泛型参数的模擦除),但也有examples不一致。因此,将
Class[_]
强制转换为Class[T]
是公平的。这是一个未经检查的强制转换,但对Class[_]
进行未经检查的强制转换并不罕见,即使在Java世界中也是如此。虽然我当然尊重您对asInstanceOf
的谨慎,但在这种情况下也没什么问题。