Force a null into non-nullable type

2019-08-08 14:54发布

问题:

Is there a way to jam a null into a non-nullable type with some sort of "I know what I'm doing"?

I want to make a doubly linked list:

data class Node(var prev: Node, var next: Node, val value: Int)

I can guarantee that this list will have at least two elements at all times except during initialization, after the first node has been added but before the second node. I'd like to make the guarantee that prev and next will never be null.

One thing I thought I'd try is to write a special constructor that initializes both the first and second nodes constructor(v1: Int, v2: Int) : this(Node(this, this, v1), v2), but that doesn't work because I can't do anything with this before entering the body.

回答1:

I'm wondering if we're doing the same adventofcode puzzle in kotlin

I used lateinit properties to allow the marble to change its position as a node in a linked list

class Marble(val marbleNumber: Long) {
    lateinit var counterClockwiseMarble: Marble
    lateinit var clockwiseMarble: Marble
}

initially with setters

class Marble(val marbleNumber: Long) {
    lateinit var counterClockwiseMarble: Marble
      private set

    lateinit var clockwiseMarble: Marble
      private set

    fun setCounterClockwise(m: Marble) {
      this.counterClockwiseMarble = m
    }

    fun setClockwise(m: Marble) {
      this.clockwiseMarble = m
    }
}

but that seemed like a lot of text when usage was controlled enough to be safe

If it is the same puzzle you can see this used in context on github



回答2:

I can create properties with null-able backing fields.

data class Node(val value: Int) {
  private var _prev: Node? = null
  var prev: Node
    get() = _prev!!
    set(value) {
      _prev = value
    }

  private var _next: Node? = null
  var next: Node
    get() = _next!!
    set(value) {
      _next = value
    }

  constructor(prev: Node, next: Node, value: Int) : this(value) {
    this.prev = prev
    this.next = next
    prev.next = this
    next.prev = this
  }
}

This can then be initialized via

val node0 = Node(0)
val node1 = Node(node0, node0, 1)

I'm hoping to learn the overhead can be reduced a little bit, but this is better than null-able next/prev.



回答3:

You could abstract the way a Node holds its references by providing an interface or a sealed class e.g.

data class Node(val ref: Reference, val value: Int)

sealed class Reference {
    class Forward(val next: Node): Reference()
    class Backward(val prev: Node): Reference()
    class Bidirection(val prev: Node, val next: Node): Reference()
}

This approach will negate your need for nullable types. It is not very performant, but a declarative way to get compile time type-safety.



标签: kotlin