В вашем примере PropsBox
не накладывает каких-либо интересных ограничений на properties
- он просто имеет член properties: Map[String, Any]
.Следовательно, нет способа обнаружить разницу между наследованием от PropsBox
и простым требованием def properties: Map[String, Any]
.
. Рассмотрим следующий пример, где на самом деле разница есть.Предположим, у нас есть два класса GoodBox
и BadBox
.
GoodBox
имеет properties
, и все ключи являются короткой строкой, которая содержит только цифры 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 ведут себя по-разному:
- версия self-type не позволяет скомпилировать код (ошибка обнаружена во время компиляции)
- версия abstract-def падает во время выполнения с
NumberFormatException
(ошибка возникает во время выполнения)
В этом разница.