参考 Java Enum 作为 Scala 中的类型参数
Reffer to Java Enum as a Type parameter in Scala
我想为 protobuf 编译器生成的枚举实现一个瘦包装器,因为我需要注释一些方法。
Protobuf 生成 final class 所以我实现了一个新的通用 class 来存储枚举实例而不是扩展枚举 subclass.
Protobuf 生成的 class 如下所示
public enum CardType
implements com.google.protobuf.ProtocolMessageEnum {
/**
* <code>NONE = 0;</code>
*/
NONE(0, 0)
}
我实现了一个包装器 class
object ProtoEnumWrapper {
def fromString[T <: Enum[T] with ProtocolMessageEnum](s: String): ProtoEnumWrapper[T] =
Enum.valueOf[T](classOf[T], s) match {
case null => throw new InvalidStringForEnum(s"${s} is not value of ${classOf[T]}")
}
def fromProto[T <: Enum[T] with ProtocolMessageEnum](proto: T): ProtoEnumWrapper[T] =
new ProtoEnumWrapper[T](proto)
class InvalidStringForEnum(message: String) extends RuntimeException(message)
}
class ProtoEnumWrapper[T <: Enum[T] with ProtocolMessageEnum](proto: T) {
override def equals(o: Any) = o match {
case x: ProtoEnumWrapper[T] => proto == x.proto
case e: T => proto == e
case s: String => toString == s
case _ => false
}
override def toString = proto.toString
}
因为我想像这样使用它
val wrapper = ProtoEnumWrapper.fromString[CardType]("NONE")
val proto = CardType.valueOf("NONE")
val wrapper2 = ProtoEnumWrapper.fromProto[CardType](proto)
wrapper == proto // true
wrapper == wrapper2 // true
wrapper == "NONE" // true
但是,这会导致编译错误
Error:(7, 29) class type required but T found
Enum.valueOf[T](classOf[T], s) match {
^
Error:(8, 84) class type required but T found
case null => throw new InvalidStringForEnum(s"${s} is not value of ${classOf[T]}")
^
Error:(19, 47) value proto is not a member of jp.pocket_change.voucher.domain.ProtoEnumWrapper[T]
case x: ProtoEnumWrapper[T] => proto == x.proto
^
有一种叫做擦除的东西——因为它,当你的代码被编译成 JVM 字节码时,里面没有 T
的踪迹。大多数时候。不过在 Scala 中,有一个解决方法:您可以指定一个隐式 scala.reflect.ClassTag
,它由编译器生成并传递给相应的方法调用。
我在您的代码中更新了 ProtoEnumWrapper.fromString
、ProtoEnumWrapper.fromProto
和 ProtoEnumWrapper
的签名:
object ProtoEnumWrapper {
def fromString[T <: Enum[T] with ProtocolMessageEnum](s: String)(implicit ct: ClassTag[T]: ProtoEnumWrapper[T] = try {
new ProtoEnumWrapper(Enum.valueOf[T](ct.runtimeClass, s))
} catch {
case e: IllegalArgumentException => throw new InvalidStringForEnum(s"${s} is not a value of ${ct.runtimeClass}")
}
def fromProto[T <: Enum[T] with ProtocolMessageEnum : ClassTag](proto: T): ProtoEnumWrapper[T] =
new ProtoEnumWrapper[T](proto)
class InvalidStringForEnum(message: String) extends RuntimeException(message)
}
class ProtoEnumWrapper[T <: Enum[T] with ProtocolMessageEnum : ClassTag](proto: T) {
override def equals(o: Any) = o match {
case x: ProtoEnumWrapper[T] => proto == x.proto
case e: T => proto == e
case s: String => toString == s
case _ => false
}
override def toString = proto.toString
}
注意 2 个替代定义:def a[T : ClassTag]
只是一种较短的写法 def a[T](implicit ct: ClassTag[T])
,以防您的代码中不需要 ct
的实际实例。
我想为 protobuf 编译器生成的枚举实现一个瘦包装器,因为我需要注释一些方法。 Protobuf 生成 final class 所以我实现了一个新的通用 class 来存储枚举实例而不是扩展枚举 subclass.
Protobuf 生成的 class 如下所示
public enum CardType
implements com.google.protobuf.ProtocolMessageEnum {
/**
* <code>NONE = 0;</code>
*/
NONE(0, 0)
}
我实现了一个包装器 class
object ProtoEnumWrapper {
def fromString[T <: Enum[T] with ProtocolMessageEnum](s: String): ProtoEnumWrapper[T] =
Enum.valueOf[T](classOf[T], s) match {
case null => throw new InvalidStringForEnum(s"${s} is not value of ${classOf[T]}")
}
def fromProto[T <: Enum[T] with ProtocolMessageEnum](proto: T): ProtoEnumWrapper[T] =
new ProtoEnumWrapper[T](proto)
class InvalidStringForEnum(message: String) extends RuntimeException(message)
}
class ProtoEnumWrapper[T <: Enum[T] with ProtocolMessageEnum](proto: T) {
override def equals(o: Any) = o match {
case x: ProtoEnumWrapper[T] => proto == x.proto
case e: T => proto == e
case s: String => toString == s
case _ => false
}
override def toString = proto.toString
}
因为我想像这样使用它
val wrapper = ProtoEnumWrapper.fromString[CardType]("NONE")
val proto = CardType.valueOf("NONE")
val wrapper2 = ProtoEnumWrapper.fromProto[CardType](proto)
wrapper == proto // true
wrapper == wrapper2 // true
wrapper == "NONE" // true
但是,这会导致编译错误
Error:(7, 29) class type required but T found
Enum.valueOf[T](classOf[T], s) match {
^
Error:(8, 84) class type required but T found
case null => throw new InvalidStringForEnum(s"${s} is not value of ${classOf[T]}")
^
Error:(19, 47) value proto is not a member of jp.pocket_change.voucher.domain.ProtoEnumWrapper[T]
case x: ProtoEnumWrapper[T] => proto == x.proto
^
有一种叫做擦除的东西——因为它,当你的代码被编译成 JVM 字节码时,里面没有 T
的踪迹。大多数时候。不过在 Scala 中,有一个解决方法:您可以指定一个隐式 scala.reflect.ClassTag
,它由编译器生成并传递给相应的方法调用。
我在您的代码中更新了 ProtoEnumWrapper.fromString
、ProtoEnumWrapper.fromProto
和 ProtoEnumWrapper
的签名:
object ProtoEnumWrapper {
def fromString[T <: Enum[T] with ProtocolMessageEnum](s: String)(implicit ct: ClassTag[T]: ProtoEnumWrapper[T] = try {
new ProtoEnumWrapper(Enum.valueOf[T](ct.runtimeClass, s))
} catch {
case e: IllegalArgumentException => throw new InvalidStringForEnum(s"${s} is not a value of ${ct.runtimeClass}")
}
def fromProto[T <: Enum[T] with ProtocolMessageEnum : ClassTag](proto: T): ProtoEnumWrapper[T] =
new ProtoEnumWrapper[T](proto)
class InvalidStringForEnum(message: String) extends RuntimeException(message)
}
class ProtoEnumWrapper[T <: Enum[T] with ProtocolMessageEnum : ClassTag](proto: T) {
override def equals(o: Any) = o match {
case x: ProtoEnumWrapper[T] => proto == x.proto
case e: T => proto == e
case s: String => toString == s
case _ => false
}
override def toString = proto.toString
}
注意 2 个替代定义:def a[T : ClassTag]
只是一种较短的写法 def a[T](implicit ct: ClassTag[T])
,以防您的代码中不需要 ct
的实际实例。