2016-04-24 10 views
3

Ich behebe gerade einen sehr seltsamen Fehler, bei dem ein private final val innerhalb eines Objektfeldes nicht initialisiert wird, bevor auf sie zugegriffen wird. Die Position des Codes kann unter https://github.com/mdedetrich/soda-time/blob/master/jvm/src/main/scala/org/joda/time/chrono/GregorianChronology.scala#L12-L33 gefunden werden.Nicht initialisierte Felder in einem Objekt

Sie können diesen Fehler simulieren, indem Sie den obigen Repo und dann sodatimeJVM/console ausführen und dann in der Konsole ausführen import org.joda.time._; DateTime.now(). MinusDays (10)

Der Code wurde hier gepostet

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) 

Die letzte Zeile, das heißt var chronos: Array[GregorianChronology] = cCache.get(_zone) eine java.lang.NullPointerException wirft. Der Wert, der null ist, ist cCache, aber das macht keinen Sinn, da es eindeutig bei private final val cCache = new ConcurrentHashMap[DateTimeZone, Array[GregorianChronology]]() initialisiert wird. Wenn ich anstelle "-Xcheckinit" Scala dann sagt mir scala.UninitializedFieldError: Uninitialized field: GregorianChronology.scala: 19 was auf private final val cCache = new ConcurrentHashMap[DateTimeZone, Array[GregorianChronology]]() zeigt. Dies ist nicht sehr nützlich, da ich weiß, dass der Wert nicht initialisiert ist, das Problem ist, dass ich nicht weiß warum. Da es sich um ein finales val handelt, gehe ich davon aus, dass es einer der ersten Werte sein sollte, die initialisiert werden, vor allem bevor getInstance jemals aufgerufen wird.

Ich weiß, dass ich den Wert faul machen kann, es zu beheben, das würde jedoch einen unnötigen Leistungseinbruch bringen. Was noch wichtiger ist, die äquivalente Java-Version private static final ConcurrentHashMap<DateTimeZone, GregorianChronology[]> cCache = new ConcurrentHashMap<DateTimeZone, GregorianChronology[]>() funktioniert absolut gut.

Antwort

3

Das Problem ist hier:

private final val INSTANCE_UTC = getInstance(DateTimeZone.UTC) 

Er ruft:

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

Welche Anrufe: noch initialisiert

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

Aber INSTANCE_UTC wird, was bedeutet, dass wir nicht erreicht haben, cCache in der Reihenfolge der Initialisierung, so cCache ist null an diesem Punkt zur Laufzeit.

Dies ist ähnlich:

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 
} 

Die Lösung zwar einfach ist, nur die Initialisierung von cCache an die Spitze des Objekts bewegen, da es nichts anderes verweisen ist. Auf diese Weise wird es immer zuerst initialisiert.

+0

Danke, sich so dumm fühlen, das zu verpassen! – mdedetrich

Verwandte Themen