Почему я не могу реализовать этот интерфейс Java в Scala без уродливых анонимных классов

У меня есть следующий интерфейс на 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 не работает с моей первой попытки, но работает при использовании в анонимном классе?
  3. Почему enum.Value и T # Value не совпадают?
  4. Почему компилятор жалуется на то, что значение T # не совпадает с Comparable [T], поскольку Value расширяет Ordered [Value], которое расширяет Comparable [Value]?

person bdew    schedule 01.02.2016    source источник
comment
Почему не class EnumerationProperty1[T <: Enumeration](val enum: T, val name: String) extends IProperty[T] . Я не понимаю, почему это не так.   -  person Jatin    schedule 01.02.2016


Ответы (1)


Ах, как приятно работать с зависимыми от пути типами ...

Enumeration#Value - это тип, зависящий от пути. То есть фактический тип Value зависит от текущего экземпляра реализации Enumeration.

Таким образом, если у вас есть два Enumerations like

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

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

следующие условия возвращают false.

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

но это правда

A.first.isInstanceOf[Enumeration#Value]

Дополнительные сведения о типах, зависящих от пути, см. В этом статья

К вопросам:

@ 1) это зависит от того, чего вы пытаетесь достичь. Быстрый способ сделать это - использовать завод. Немного похоже на ваш "анонимный" пример, но немного более скала-иш:

// 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) Потому что в первом примере enum является параметром конструктора, а во втором примере это локальный val. Вспомните, как будет выглядеть определение класса в java:

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

Здесь ясно, почему enum нельзя узнать до вызова конструктора.

@ 3) см. Выше: типы, зависимые от пути

@ 4) Боюсь, это немного выше меня. Но я готов поспорить, что это связано с Enumeration#Value зависимостью от пути и вуду, выполненным с помощью #;)

person Sascha Kolberg    schedule 01.02.2016
comment
Я думаю 4: Comparable[Value] не Comparable[Enumeration#Value]. Он сохраняет явный внешний указатель для целей equals; вы можете представить compareTo(Enum#Value) сортировку по имени класса перечисления, затем по порядковому номеру или подобной странности. # 2 то есть область действия класса param. - person som-snytt; 01.02.2016