2016-04-24 13 views
3

現在、オブジェクトフィールド内のprivate final valがアクセスされる前に初期化されていない非常に奇妙なエラーが修正されています。コードの場所はhttps://github.com/mdedetrich/soda-time/blob/master/jvm/src/main/scala/org/joda/time/chrono/GregorianChronology.scala#L12-L33です。オブジェクト内の初期化されていないフィールド

上記エラーをシミュレートしてsodatimeJVM/consoleを実行してから、コンソールで `import org.joda.time._; DateTime.now()。minusDays(10)

コード、すなわちvar chronos: Array[GregorianChronology] = cCache.get(_zone)java.lang.NullPointerExceptionをスローし、ここで

object GregorianChronology { 

    private final val MILLIS_PER_YEAR = (365.2425 * DateTimeConstants.MILLIS_PER_DAY).toLong 
    private final val MILLIS_PER_MONTH = (365.2425 * DateTimeConstants.MILLIS_PER_DAY/12).toLong 
    private final val DAYS_0000_TO_1970 = 719527 
    private final val MIN_YEAR = -292275054 
    private final val MAX_YEAR = 292278993 
    private final val INSTANCE_UTC = getInstance(DateTimeZone.UTC) 

    private final val cCache = new ConcurrentHashMap[DateTimeZone, Array[GregorianChronology]]() 

    def getInstanceUTC(): GregorianChronology = INSTANCE_UTC 

    def getInstance(): GregorianChronology = getInstance(DateTimeZone.getDefault, 4) 

    def getInstance(zone: DateTimeZone): GregorianChronology = getInstance(zone, 4) 

    def getInstance(zone: DateTimeZone, minDaysInFirstWeek: Int): GregorianChronology = { 
    var _zone: DateTimeZone = zone 
    if (_zone == null) { 
     _zone = DateTimeZone.getDefault 
    } 
    var chrono: GregorianChronology = null 
    var chronos: Array[GregorianChronology] = cCache.get(_zone) 

最後の行を掲載されています。 nullの値はcCacheですが、これは明らかにprivate final val cCache = new ConcurrentHashMap[DateTimeZone, Array[GregorianChronology]]()で初期化されているので意味がありません。 "-Xcheckinit" Scalaをオンにすると、private final val cCache = new ConcurrentHashMap[DateTimeZone, Array[GregorianChronology]]()を指すscala.UninitializedFieldError: Uninitialized field: GregorianChronology.scala: 19と表示されます。値が初期化されていないことを知っているので、これはあまり役に立ちません。問題はわかりません。なぜですか?その最終的な値から、特に初期化された最初の値の1つであることが前提です。特に、getInstanceが呼び出される前です。

私はそれを修正するために怠惰な値を作ることができますが、それは不必要なパフォーマンスヒットを招くことになります。さらに重要なのは、同等のJavaバージョンprivate static final ConcurrentHashMap<DateTimeZone, GregorianChronology[]> cCache = new ConcurrentHashMap<DateTimeZone, GregorianChronology[]>()が問題なく動作することです。

答えて

3

問題はここにある:

private final val INSTANCE_UTC = getInstance(DateTimeZone.UTC) 

それは呼び出します。呼び出す

def getInstance(zone: DateTimeZone): GregorianChronology = getInstance(zone, 4) 

def getInstance(zone: DateTimeZone, minDaysInFirstWeek: Int): GregorianChronology = { 
    .. 
    var chronos: Array[GregorianChronology] = cCache.get(_zone) 
    .. 
} 

しかしINSTANCE_UTCはまだ初期化されている、我々は達していないことを意味しますcCacheは初期化順であるため、cCacheはですその時点での実行時の。解決策は、それが何かを参照していないので、ちょうど、オブジェクトの先頭にcCacheの初期化を移動する、しかし簡単です

object Test { 
    val a = foo("a") // Calls a def which references and uses an uninitialized val, NPE 
    val b = "b" 
    def foo(c: String): Int = b.length + c.length 
} 

これは似ています。こうすることで、常に最初に初期化されます。

+0

ありがとう、それを逃して愚かな気分! – mdedetrich

関連する問題