假设我有几个自动生成的类,例如MyEnum1
,MyEnum2
,...(它们不一定是Scala枚举类型,只是一些自动生成的类)。尽管MyEnum1
的类型与MyEnum2
的类型不同(并且除Any
之外它们没有其他自动生成的父类型),但我可以保证所有这些自动生成的类型都具有完全相同的公共静态方法,尤其是findById
和findByName
,它们允许根据索引或字符串名称查找枚举值。
我正在尝试创建一个函数,该函数将利用findById
和findByName
的特定于类型的版本,但是可以接受MyEnum1
,MyEnum2
中的任何一个, ...作为函数参数。
请注意,典型的sealed trait
+ case class
模式从不同的枚举中创建求和类型将无济于事,因为我正在谈论基于类型参数调度不同的静态方法,并且根本不会涉及任何实际值参数。
例如,假设MyEnum1
编码男性/女性性别。这样MyEnum1.findById(0)
返回类型为MyEnum1.Female
的{{1}}。并说MyEnum1
编码眼睛的颜色,以便MyEnum2
返回类型为MyEnum2.findById(0)
的{{1}}。
为我提供了一个Map,其键为 type ,值是要查找的索引,例如
MyEnum2.Green
我想一般地这样做:
MyEnum2
并返回一些类似于
的序列类型(可以具有元素类型val typeMap = Map(
MyEnum1 -> 0,
MyEnum2 -> 0
)
)
for ( (elemType, idx) <- typeMap ) yield elemType.findById(v)
|---------------|
the goal is to
avoid boilerplate
of defining this
with different
pattern matching
for every enum.
我在Any
+ MyEnum1.Female, MyEnum2.Green, ...
样板中苦苦挣扎了一段时间,这在概念上似乎并不是正确的方法。无论我将sealed trait
或case class
的 values 包装到诸如MyEnum1
这样的case类值构造函数中,并尝试定义用于对该 value < / strong>,当我想执行MyEnum2
时,在上面的代码示例中无济于事,因为编译器仍会说类型FromMyEnum1(e: MyEnum1)
(它将解析为我的{{1 }}),没有方法elemType.findById(...)
。
我强烈希望不要将类型本身包装在case类模式中以用作键,但是我可以做到这一点-除非我看不到如何将类型本身视为第一类值,否则案例类构造函数,类似天真的
Any
(这样,Map
键的类型可能为findById
,并且可能存在一些隐式变量,它们使每个案例类构造函数与case class FromMyEnum1(e: MyEnum1.getClass) extends EnumTrait
或Map
的正确实现相匹配。 )。
任何帮助您了解Scala如何启用使用类型本身作为case类值构造函数内部的值的帮助!
答案 0 :(得分:1)
您的问题中存在一些基本的误解。
首先,Scala中没有“静态方法”,所有方法都附加到类的实例上。如果您想要一个类的每个实例都相同的方法,则可以向该类的伴随对象添加一个方法,然后在该对象上调用它。
第二,您不能在类型上调用方法,只能在类型的实例上调用方法。因此,您无法在findById
类型之一上调用MyEnum
,而只能在这些类型之一的实例上调用它。
第三,您不能从方法中返回类型,只能返回类型的实例。
很难准确说明您要实现的目标,但是我怀疑MyEnum1
,MyEnum2
应该是对象,而不是类。这些继承自您定义的通用接口(findById
,findByName
)。然后,您可以从普通类型的实例到要在Map
调用中使用的索引创建一个findById
。
示例代码:
trait MyEnum {
def findById(id: Int): Any
def findByName(name: String): Any
}
object MyEnum1 extends MyEnum {
trait Gender
object Male extends Gender
object Female extends Gender
def findById(id: Int): Gender = Male
def findByName(name: String): Gender = Female
}
object MyEnum2 extends MyEnum {
trait Colour
object Red extends Colour
object Green extends Colour
object Blue extends Colour
def findById(id: Int): Colour = Red
def findByName(name: String): Colour = Blue
}
val typeMap = Map(
MyEnum1 -> 0,
MyEnum2 -> 0,
)
for ((elemType, idx) <- typeMap ) yield elemType.findById(idx)
如果您不能提供共同的父母trait
,请使用结构类型:
object MyEnum1 {
trait Gender
object Male extends Gender
object Female extends Gender
def findById(id: Int): Gender = Male
def findByName(name: String): Gender = Female
}
object MyEnum2 {
trait Colour
object Red extends Colour
object Green extends Colour
object Blue extends Colour
def findById(id: Int): Colour = Red
def findByName(name: String): Colour = Blue
}
type MyEnum = {
def findById(id: Int): Any
def findByName(name: String): Any
}
val typeMap = Map[MyEnum, Int](
MyEnum1 -> 0,
MyEnum2 -> 0,
)
for ((elemType, idx) <- typeMap) yield elemType.findById(idx)
答案 1 :(得分:0)
如果类具有实际实例(单对象计数),则可以使用结构类型:
np.add(a, 2, out=b)
因此,如果仅使用Scala,事情就很简单。
但是Java类没有伴随对象-您最终会得到type Enum[A] = {
def findById(id: Int): E
def findByName(name: String): E
def values(): Array[E]
}
trait SomeEnum
object SomeEnum {
case object Value1 extends SomeEnum
case object Value2 extends SomeEnum
def findById(id: Int): SomeEnum = ???
def findByName(name: String): SomeEnum = ???
def values(): Array[SomeEnum] = ???
}
trait SomeEnum2
object SomeEnum2 {
case object Value1 extends SomeEnum2
case object Value2 extends SomeEnum2
def findById(id: Int): SomeEnum2 = ???
def findByName(name: String): SomeEnum2 = ???
def values(): Array[SomeEnum2] = ???
}
val x: Enum[SomeEnum] = SomeEnum
val y: Enum[SomeEnum2] = SomeEnum2
。这行不通。为此,您将必须使用反射,因此在所有情况下保持API一致性都存在问题。
但是,您可以做的是这样的:
定义一组通用的操作,例如
object mypackage.MyEnum is not a value
分开处理每个案件:
trait Enum[A] {
def findById(id: Int): A = ???
def findByName(name: String): A = ???
def values(): Array[A] = ???
}
创建一个伴随对象,并使这些情况用隐式处理:
def buildJavaEnumInstance[E <: java.util.Enum: ClassTag]: Enum[E] = new Enum[E] {
// use reflection here to implement methods
// you dont
}
def buildCoproductEnum = // use shapeless or macros to get all known instances
// https://stackoverflow.com/questions/12078366/can-i-get-a-compile-time-list-of-all-of-the-case-objects-which-derive-from-a-sea
...
使用object Enum {
def apply[E](implicit e: Enum[E]): Enum[E] = e
implicit def buildJavaEnumInstance[E <: java.util.Enum: ClassTag] = ???
implicit def buildCoproductEnum = ???
...
}
作为类型类或其他类型。
Enum
不过,我同意,这将需要大量的前期编码。对于图书馆来说,这可能是一个好主意,因为我相信不仅您遇到这个问题。