Ограничение классов, которые могут расширить черту scala - PullRequest
0 голосов
/ 19 сентября 2018

Похоже, есть три (или более) способа ограничить, какие классы могут смешиваться с данной чертой scala:

  1. Использование общего предка [trait]
  2. Использование абстрактныхобъявление
  3. Использование в типе черты self-type

Метод общего предка требует дополнительных ограничений и кажется неоптимальным.Между тем как самопечатание, так и абстрактные объявления кажутся идентичными.Может ли кто-нибудь объяснить разницу и варианты использования (особенно между 2 и 3)?

Мой пример:

val exampleMap = Map("one" -> 1, "two" -> 2)
class PropsBox (val properties : Map[String, Any])    

// Using Common Ancestor
trait HasProperties {
  val properties : Map[String, Any]
}
trait KeysAsSupertype extends HasProperties {
  def keys : Iterable[String] = properties.keys
}
class SubProp(val properties : Map[String, Any]) extends HasProperties
val inCommonAncestor = new SubProp(exampleMap) with KeysAsSupertype
println(inCommonAncestor.keys)
// prints: Set(one, two)


// Using Abstract Declaration
trait KeysAsAbstract {
  def properties : Map[String, Any]
  def keys : Iterable[String] = properties.keys
}
val inAbstract = new PropsBox(exampleMap) with KeysAsAbstract
println(inSelfType.keys)
// prints: Set(one, two)    


// Using Self-type
trait KeysAsSelfType {
  this : PropsBox => 
  def keys : Iterable[String] = properties.keys
}
val inSelfType = new PropsBox(exampleMap) with KeysAsSelfType
println(inSelfType.keys)
// prints: Set(one, two)    

1 Ответ

0 голосов
/ 19 сентября 2018

В вашем примере PropsBox не накладывает каких-либо интересных ограничений на properties - он просто имеет член properties: Map[String, Any].Следовательно, нет способа обнаружить разницу между наследованием от PropsBox и простым требованием def properties: Map[String, Any].

. Рассмотрим следующий пример, где на самом деле разница есть.Предположим, у нас есть два класса GoodBox и BadBox.

  1. GoodBox имеет properties, и все ключи являются короткой строкой, которая содержит только цифры
  2. BadBox просто имеет properties и не гарантирует ничего о структуреключей

В коде:

/** Has `properties: Map[String, Any]`, 
  * and also guarantees that all the strings are
  * actually decimal representations of numbers 
  * between 0 and 99.
  */
class GoodBox(val properties: Map[String, Any]) {
  require(properties.keys.forall {
    s => s.forall(_.isDigit) && s.size < 3
  })
}


/** Has `properties: Map[String, Any]`, but 
  * guarantees nothing about the keys.
  */
class BadBox(val properties: Map[String, Any])

Теперь предположим, что по какой-то причине мы хотим преобразовать Map[String, Any] в малонаселенный Array[Any] и использовать ключи какиндексы массива.Здесь, опять же, есть два способа сделать это: один с объявлением self -типа, а другой с абстрактным объявлением def properties:

trait AsArrayMapSelfType {
  self: GoodBox =>
  def asArrayMap: Array[Any] = {
    val n = 100
    val a = Array.ofDim[Any](n)
    for ((k, v) <- properties) {
      a(k.toInt) = v
    }
    a
  }
}

trait AsArrayMapAbstract {
  def properties: Map[String, Any]
  def asArrayMap: Array[Any] = {
    val n = 100
    val a = Array.ofDim[Any](n)
    for ((k, v) <- properties) {
      a(k.toInt) = v
    }
    a
  }
}

Теперь попробуйте:

val goodBox_1 = 
  new GoodBox(Map("1" -> "one", "42" -> "fourtyTwo"))
  with AsArrayMapSelfType

val goodBox_2 = 
  new GoodBox(Map("1" -> "one", "42" -> "fourtyTwo"))
  with AsArrayMapAbstract

/* error: illegal inheritance
val badBox_1 = 
  new BadBox(Map("Not a number" -> "mbxkxb"))
  with AsArrayMapSelfType
*/

val badBox_2 = 
  new BadBox(Map("Not a number" -> "mbxkxb"))
  with AsArrayMapAbstract

goodBox_1.asArrayMap
goodBox_2.asArrayMap
// badBox_1.asArrayMap - not allowed, good!
badBox_2.asArrayMap // Crashes with NumberFormatException, bad

При goodBox оба метода будут работать и давать одинаковые результаты.Тем не менее, с badBox, self-type и abstract-def ведут себя по-разному:

  1. версия self-type не позволяет скомпилировать код (ошибка обнаружена во время компиляции)
  2. версия abstract-def падает во время выполнения с NumberFormatException (ошибка возникает во время выполнения)

В этом разница.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...