Scala 3中使用擦除类型进行模式匹配的标准方法是什么?
问题描述
先看示例代码:
object MatchErasedType { trait Supe { self: Singleton => type T1 lazy val default: T1 def process(v: Any): T1 = { v match { case vv: T1 => vv case _ => default } } } }
这段代码会触发编译器警告:
MatchErasedType.scala:13:14: the type test for Supe.this.T1 cannot be checked at runtime
由于Supe的所有实例都被保证是Singleton类型,process方法的延迟具体化不存在擦除问题。问题分为两种场景:
- 若所有
Supe实例都是特化的Singleton类型,如何在不使用隐式召唤或转换的情况下消除该警告? - 其他场景下如何消除该警告?
更新说明:v: Any的类型在编译期无法确定,调用端无法提供此类信息,因此本问题不涉及T1无法解析为具体类或运行时条件的情况。
解决方案
场景1:所有Supe实例都是特化的Singleton类型
因为Singleton特质保证了实例的T1是具体且唯一的类型,我们可以直接告诉编译器该类型检查是安全的,有两种简单方式:
方法1:添加@unchecked注解
在模式匹配的类型检查后加上@unchecked,抑制编译器的擦除警告:
def process(v: Any): T1 = { v match { case vv: T1 @unchecked => vv case _ => default } }
方法2:直接使用强制类型转换
既然确定类型匹配是可靠的,用asInstanceOf替代模式匹配,同时捕获类型转换异常返回默认值:
def process(v: Any): T1 = { try { v.asInstanceOf[T1] } catch { case _: ClassCastException => default } }
场景2:其他场景(存在非Singleton的Supe实例)
这种情况下需要引入运行时类型信息来做可靠的类型检查,以下是两种实现方式:
使用ClassTag
在trait Supe内部绑定ClassTag[T1]成员,实现类中提供具体的类型标签,利用ClassTag的unapply方法完成类型检查:
import scala.reflect.ClassTag object MatchErasedType { trait Supe { self: Singleton => type T1 implicit val t1Tag: ClassTag[T1] lazy val default: T1 def process(v: Any): T1 = { t1Tag.unapply(v).getOrElse(default) } } // 示例实现类 class ConcreteSupe extends Supe with Singleton { type T1 = String override val t1Tag: ClassTag[String] = ClassTag(classOf[String]) override lazy val default: String = "default" } }
使用TypeTag(支持泛型类型检查)
如果需要处理泛型类型的检查,可以用TypeTag来获取更精确的运行时类型信息:
import scala.reflect.runtime.universe._ object MatchErasedType { trait Supe { self: Singleton => type T1 implicit val t1Tag: TypeTag[T1] lazy val default: T1 def process(v: Any): T1 = { val mirror = runtimeMirror(getClass.getClassLoader) val vType = mirror.classSymbol(v.getClass).toType if (vType <:< t1Tag.tpe) v.asInstanceOf[T1] else default } } // 示例实现类 class ConcreteSupe extends Supe with Singleton { type T1 = List[Int] override val t1Tag: TypeTag[List[Int]] = typeTag[List[Int]] override lazy val default: List[Int] = Nil } }
内容的提问来源于stack exchange,提问作者tribbloid
相关产品推荐
相关产品推荐

