为什么我不能在没有丑陋的匿名 类 的情况下在 Scala 中实现这个 Java 接口

Why can't I implement this Java interface in Scala without ugly anonymous classes

我在Java

中有如下界面
public interface IProperty<T extends Comparable<T>> {
    String getName();

    Collection<T> getAllowedValues();

    Class<T> getValueClass();

    String getName(T value);
}

并尝试在 Scala 中实现它,但无法让它工作


第一次尝试:

class EnumerationProperty1[T <: Enumeration](val enum: T, val name: String) extends IProperty[enum.Value] {
  override def getName = name
  override def getValueClass = classOf[enum.Value]
  override def getName(value: enum.Value): String = value.toString
  override def getAllowedValues: java.util.Collection[enum.Value] = enum.values.toList
}

没有编译错误:未找到:值枚举


第二次尝试:

class EnumerationProperty2[T <: Enumeration](val enum: T, val name: String) extends IProperty[T#Value] {
  override def getName = name
  override def getValueClass = classOf[T#Value]
  override def getName(value: T#Value): String = value.toString
  override def getAllowedValues: java.util.Collection[T#Value] = enum.values.toList
}

编译时出现错误:类型参数 [T#Value] 不符合特征 IProperty 的类型参数范围 [T <: Comparable[T]]


最后我想出了一个办法,但我觉得它很难看:

object EnumerationPropertyAnonymous {
  def create[T <: Enumeration](enum: T, name: String) = {
    new IProperty[enum.Value] {
      override def getName = name
      override def getValueClass = classOf[enum.Value]
      override def getName(value: enum.Value): String = value.toString
      override def getAllowedValues: java.util.Collection[enum.Value] = enum.values.toList
    }
  }
}

问题:

  1. 正确的做法是什么?
  2. 为什么 enum.Value 在我第一次尝试时不起作用,但在匿名 class 中使用时却起作用?
  3. 为什么 enum.Value 和 T#Value 不一样?
  4. 为什么编译器抱怨 T#Value 不匹配 Comparable[T],因为 Value 扩展了 Ordered[Value] 而后者扩展了 Comparable[Value]?

啊,使用 路径依赖类型 的乐趣...

Enumeration#Value 是路径依赖类型。 也就是说 Value 的实际类型取决于 Enumeration 的当前实例的实现。

因此,如果您有两个 Enumerations 喜欢

object A extends Enumeration {
  val first = Value(0, "first")
}

object B extends Enumeration {
  val first = Value(0, "first")
}

以下条件returnfalse.

A.first == B.first
A.first.isInstanceOf[B.first.type]

但这是真的

A.first.isInstanceOf[Enumeration#Value]

有关路径依赖类型的更多信息,请参阅此article

问题:

@1) 这取决于您要完成的任务。一个快速的方法是与工厂合作。有点类似于您的 'anonymous' 示例,但更像 scala:

// it is recommended to use converters instead of conversions.
import scala.collection.JavaConverters._

case class EnumPropertyFactory[T <: Enumeration](val enum: T) {
  def apply(name: String) = new EnumerationProperty(name)

  class EnumerationProperty(val name: String) extends IProperty[enum.Value] {
    override def getName = name
    override def getValueClass = classOf[enum.Value]
    override def getName(value: enum.Value): String = value.toString
    override def getAllowedValues: java.util.Collection[enum.Value] = enum.values.toList.asJavaCollection
  }
}

// can be used with something like
val enum1PropertyFactory = EnumPropertyFactory(EnumOne)
val foo = enum1PropertyFactory("foo")
val bar = enum1PropertyFactory("bar")

@2) 因为在第一个示例中枚举是一个构造函数参数,而在第二个示例中它是一个本地 val。记住 class 定义在 java:

中的样子
class EnumerationProperty1<T extends Enumeration> extends IProperty<enum.Value> {
    public EnumerationProperty1(T enum, String name) { ... }
}

这里就很清楚了,为什么enum在调用构造函数之前无法知道

@3) 见上:路径依赖类型

@4) 恐怕有点过头了。但我敢打赌,这与 Enumeration#Value 依赖于路径以及 # 完成的伏都教有关 ;)