2016-09-19 3 views
3

Я нашел ошибку при инициализации «рукописного перечисления» в scala. Это рукописный, потому что это sealed case class и object SomeCode, у которых есть объекты внутри.Ошибка инициализации объекта Scala

Что интересно - если sealed case class имеют значения по умолчанию - происходит некоторая темная магия. Например, мы имеем «рукописную нумерацию», которая выглядит как:

sealed case class SomeCode(
    id: String, 
    legend: String) 

object SomeCode { 

    object First  extends SomeCode(id = "First", legend = "first legend") 
    object Second  extends SomeCode(id = "Second", legend = "second legend") 

    val values = Seq(
    SomeCode.First, 
    SomeCode.Second) 

    private val CACHE: Map[String, SomeCode] = { 
    val ids = values.map(_.id) 
    (ids zip values).toMap 
    } 

    def getById(id: String): Option[SomeCode] = CACHE.get(id) 
} 

И одно испытание:

import com.blabla.SomeCode 
import org.scalatest.{Matchers, WordSpec} 

class SomeCodeTest extends WordSpec with Matchers { 
    "SomeCode" should { 
    "work properly" in { 
     println(SomeCode.First.toString) 
    } 
    } 
} 

Когда мы называем SomeCode.Fist инициализация object SomeCode начинается. Таким образом, инициализация для val values и private val CACHE начинается тоже, и все нормально.

Но, если ввести по умолчанию значение для нашего sealed case class ...:

sealed case class SomeCode(
    id: String, 
    legend: String = "default legend") 
... 
object First  extends SomeCode(id = "First") 
object Second  extends SomeCode(id = "Second") 

и запустить наш тест сейчас - мы будем иметь NPE в CACHE.

В тесте мы называем SomeCode.First - и это значение будет в valuesnull, так values.map(_.id) выбросит NPE. Если из тестов мы будем называть SomeCode.Second тогда null будет SomeCode.Second

Я знаю, что это не очень хорошая практика распространяется sealed case class, но тем не менее он должен работать правильно. Возможно, я не понимаю что-то в scala, но в настоящее время это похоже на ошибку компилятора для меня.

scalaVersion := "2.11.7"

Также я создаю проблемы в Ла Скала-лана: https://issues.scala-lang.org/browse/SI-9929

+0

Может быть, что-то не хватает в ваших примерах кода? Потому что, с указанным кодом, я не могу воспроизвести описанное вами поведение. Другими словами: «Хорошо работает для меня» –

+0

@SaschaKolberg отлично работает для вас со значением по умолчанию - 'legend: String =" default legend ")' и не устанавливает это значение в расширенных объектах – invis

+0

нет, это не так. –

ответ

2

Проблема происходит потому, что, когда мы называем SomeCode.First.toString, то SomeCode.First оседает в null в следующем коде:

val values = Seq(SomeCode.First, SomeCode.Second)

Вы можете проверить вышеприведенное, выполнив печать значений значений в REPL.

Интересно, посмотрите на вывод REPL со следующим кодом:

sealed case class SomeCode(
    id: String, 
    legend: String = "default") 

object SomeCode { 
    println("begin object init") 
    object First extends SomeCode(id = "First"){println("initializing First")} 
    object Second extends SomeCode(id = "Second"){println("initializing Second")} 
    println(SomeCode.First) 
    println(SomeCode.Second) 
} 

SomeCode.First 

// REPL Output: 
begin object init 
null <--- SomeCode.First is null!!! Why does it not initialize the First object? 
initializing Second <--- Correctly initializes the Second object. 
SomeCode(Second,default) 
initializing First <--- Now starts to initialize the First object 
res41: SomeCode.First.type = SomeCode(First,default) 
+0

Спасибо, в этом суть моего вопроса. Интересно, что если вы измените First на 'object First extends SomeCode (id =" First "," notDefault ")' все будет хорошо; ИЛИ переименовать 'object SomeCode {' в нечто вроде 'object SomeCodes {' – invis

+0

haha ​​Я тоже попробовал вариант «SomeCodes», и он действительно работает. Но не знаю, что происходит. – Samar

 Смежные вопросы

  • Нет связанных вопросов^_^