从Scala泛型参数获取完全限定的类对象

x759pob2  于 12个月前  发布在  Scala
关注(0)|答案(1)|浏览(171)

我试图创建一个接口,在运行时,我需要一个与泛型类相对应的类对象。
下面的代码显示了我的问题。

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,这通常是一种代码气味。它只是把类型参数放回我们知道是正确的。

fhity93d

fhity93d1#

你提到的强制转换是因为运行时类不一定对应于Scala类型。ClassTag[T]中的T是Scala类型,而Class[U]中的U是擦除的Java端类。虽然这两种类型通常一致(泛型参数的模擦除),但也有examples不一致。
因此,将Class[_]强制转换为Class[T]是公平的。这是一个未经检查的强制转换,但对Class[_]进行未经检查的强制转换并不罕见,即使在Java世界中也是如此。虽然我当然尊重您对asInstanceOf的谨慎,但在这种情况下也没什么问题。

相关问题