使用Kotlin进行值初始化

时间:2018-03-05 18:43:24

标签: kotlin

我想我发现了一个奇怪的边缘情况:

我有什么:

class Day constructor(cal: Calendar, refCal: Calendar) {
    val cal: Calendar
    val isBefore: Boolean
    val isAfter: Boolean

    init {
        this.cal = cal.clone() as Calendar
        isBefore = 0 < cal.compareTo(refCal)
        isAfter = 0 > cal.compareTo(refCal)
    }
}

我会用两个日期来实例化这一个,一个是参考,并确定日期是在参考日期之前还是之后。但是,我在执行时发现的是isBeforeisAfter在某些情况下仍然是 ,与它们的价值无关,除非我一步一步地进行使用调试器。所以,显然init在构造函数之后被调用,只是延迟了我的值不能被设置?

我要解决的问题只是在getter中计算isBeforeisAfter

class Day constructor(cal: Calendar, refCal: Calendar) {
    val cal: Calendar

    init {
        this.cal = cal.clone() as Calendar
        this.refCal = refCal.clone() as Calendar
    }

    val isBefore: Boolean
        get() { return 0 < cal.compareTo(refCal) }
    val isAfter: Boolean
        get() { return 0 > cal.compareTo(refCal) }
}

我想知道我的假设是否正确,如果有一种方法可以在用init初始化之前调用一个值,如果有的话,是否有办法来缓解这种情况。

致电示例:

fun setDates(refDate: Calendar) {
    val cal = Calendar.getInstance()
    cal.set(refDate.get(Calendar.YEAR), refDate.get(Calendar.MONTH), refDate.get(Calendar.DAY_OF_MONTH), 0, 0, 0)

    cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
    val monday = Day(cal, refDate)
    //...

1 个答案:

答案 0 :(得分:2)

据我所知,init块将在主构造函数中执行,因此原始类Day应该没问题。你可以删除init块并直接使用属性的声明分配值。

我只看到两个问题,一个是:

  

但是,我在执行时发现的是isBefore和isAfter   在某些情况下仍然是假的

如果cal.compareTo(refCal)返回0,那将毫无意外。
或者,如果{2}行的执行之间refCal的值发生了变化

isBefore = 0 < cal.compareTo(refCal)
isAfter = 0 > cal.compareTo(refCal)

第二个问题是你的第二个Day实现,当你为属性

声明一个getter时
val isBefore: Boolean
    get() { return 0 < cal.compareTo(refCal) }

您每次阅读该属性时都会比较该值,这意味着isBefore的值很容易在同一个Day实例中更改,如果值refCal属性更改。如果您只想读取refCal的值并相应地分配isBefore,那么在初始化属性时,应该在init块内或声明内(也可以省略类型和使用) >运营商直接):

class Day constructor(cal: Calendar, refCal: Calendar) {
    private val cal = cal.clone() as Calendar
    val isBefore = cal > refCal
    val isAfter = cal < refCal
}

并且可能添加val isEqual = !isBefore && !isAfter之类的属性,因为这些属性不是互斥的。

<强>更新

我刚用Kotlin 1.2.30查看,Intellij的Kotlin插件允许您查看编译Kotlin类的字节码(该操作被称为&#34;显示Kotlin字节码&#34;)。您放在init块中的代码确实在主构造函数中执行。例如这个类:

class Day(cal: Calendar)  {

    val cal: Calendar

    init {
        this.cal = cal.clone() as Calendar
    }
}

编译此构造函数:

  // access flags 0x1
  public <init>(Ljava/util/Calendar;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 1
    LDC "cal"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 3 L1
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L2
    LINENUMBER 8 L2
    ALOAD 0
    ALOAD 1
    INVOKEVIRTUAL java/util/Calendar.clone ()Ljava/lang/Object;
    DUP
    IFNONNULL L3
    NEW kotlin/TypeCastException
    DUP
    LDC "null cannot be cast to non-null type java.util.Calendar"
    INVOKESPECIAL kotlin/TypeCastException.<init> (Ljava/lang/String;)V
    ATHROW
   L3
    CHECKCAST java/util/Calendar
    PUTFIELD Day.cal : Ljava/util/Calendar;
   L4
    RETURN
   L5
    LOCALVARIABLE this LDay; L0 L5 0
    LOCALVARIABLE cal Ljava/util/Calendar; L0 L5 1
    MAXSTACK = 5
    MAXLOCALS = 2

正如您所看到的那样,Calendar.clone被调用,因此您可以像使用Java中的构造函数一样处理init

如果您没有主要构造函数,例如:

class Day {
    constructor(cal: Calendar)
    constructor()

    val cal: Calendar

    init {
        this.cal = Calendar.getInstance()
    }
}

创建了2个构造函数,并在每个构造函数中执行init块的代码。

  // access flags 0x1
  public <init>(Ljava/util/Calendar;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 1
    LDC "cal"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 4 L1
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L2
    LINENUMBER 10 L2
    ALOAD 0
    INVOKESTATIC java/util/Calendar.getInstance ()Ljava/util/Calendar;
    DUP
    LDC "Calendar.getInstance()"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    PUTFIELD Day.cal : Ljava/util/Calendar;
   L3
    RETURN
   L4
    LOCALVARIABLE this LDay; L0 L4 0
    LOCALVARIABLE cal Ljava/util/Calendar; L0 L4 1
    MAXSTACK = 4
    MAXLOCALS = 2

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 5 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 10 L1
    ALOAD 0
    INVOKESTATIC java/util/Calendar.getInstance ()Ljava/util/Calendar;
    DUP
    LDC "Calendar.getInstance()"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    PUTFIELD Day.cal : Ljava/util/Calendar;
   L2
    RETURN
   L3
    LOCALVARIABLE this LDay; L0 L3 0
    MAXSTACK = 4
    MAXLOCALS = 1