0

I am trying to port an existing Swift code to Kotlin and I'd like to use best practice for the following Swift code:

struct Deck {

    private(set) var cards: [Card]

    var cardsCount: Int {
        return self.cards.count
    }

    init(cards: [Card] = []) {
        self.cards = cards
    }

    mutating func add(card: Card) {
        self.cards.append(card)
    }
}

The design goals are:

  1. cards property is not modifiable outside of the class so its type should be List<Card>

  2. fun add(card: Card) should modify the internal cards list

Is there a way to achieve this in Kotlin without using two separate properties - one private var mutableCards: MutableList<Card> and one computed property val cards: List<Card> get() = this.mutableCards

I need some best practice for such situation.

mugx
  • 9,869
  • 3
  • 43
  • 55
plamkata__
  • 729
  • 5
  • 13
  • FYI you can drop `self.` in most of this. You only need it for the init method. – Fogmeister Dec 10 '17 at 11:26
  • Actually the best practice in Kotlin is to use two separate properties. The example in the accepted answer works, but it's not the recommended style. – yole Dec 11 '17 at 16:56

1 Answers1

0

Since the read-only List is "under the hood" also a mutable list, you may want to leverage on casting to a MutableList, doing so:

class Card {
}

class Deck(cards:List<Card>){
    var cards:List<Card>

    init {
       this.cards = cards
    }

    public fun add(card:Card) {
      (cards as MutableList<Card>).add(card)
   }
}

fun main(args: Array<String>) {    
   var cards:List<Card> = arrayListOf()
   // here I can't modify cards
   var deck = Deck(cards)
   deck.add(Card())
   print(deck.cards.count()) // printing 1
}

to be able to test it, copy and paste here.

mugx
  • 9,869
  • 3
  • 43
  • 55
  • I'd like to not have additional copy of the list – plamkata__ Dec 10 '17 at 11:41
  • 1
    I am curious because don't know Kotlin: Do those solutions really add an element to the `cards` property, or just to a mutable copy? – Martin R Dec 10 '17 at 12:07
  • @AndreaMugnaini - this seems better now. One more question - how can implement the constructor? It needs to take one argument `cards: List` – plamkata__ Dec 10 '17 at 12:18
  • @Martin R as far as I know solution1 is creating a copy of the array, instead solution2 should only expose the mutable state – mugx Dec 10 '17 at 12:27
  • @AndreaMugnaini: But doesn't that mean that any user of the class can mutate the property using the same technique, thus bypassing the read-only restriction? – Martin R Dec 10 '17 at 12:29
  • @Martin R In theory yes – mugx Dec 10 '17 at 12:30
  • @AndreaMugnaini - what if the List in the constructor is not MutableList? Then the casting later will fail. I'd guess that the constructor needs to take `cards: List` argument but create a mutable copy and assign it to the cards property. However if Deck is data class then how it should look like? – plamkata__ Dec 10 '17 at 12:36
  • @AndreaMugnaini - if replace `var cards:List = listOf()` in main method than it crashes because of the cast in `add`. Need to copy the cards in the constructor to mutable list. However if make the class to be data class and it fails compiling. Do you have any idea if can fix this ? – plamkata__ Dec 10 '17 at 13:11
  • @AndreaMugnaini - I'd like to create a Deck with list of cards. The deck internally can modify itself but as API it should be able to be constructed with any collection of cards so there is no need this list to be mutable at all for the outside world – plamkata__ Dec 10 '17 at 13:30
  • 1
    FYI, Kotlin's collections are read-only but not immutable. Refers to: https://stackoverflow.com/questions/33727657/kotlin-and-immutable-collections – BakaWaii Dec 10 '17 at 13:33
  • @AndreaMugnaini - this is what the client of Deck will decide. I'd guess it can be listOf(Card(), Card(), ...). The Deck should not be aware of this. All the Deck want is this list of cards in the constructor and it should not matter if this list is mutable, immutable, readonly or whatever. – plamkata__ Dec 10 '17 at 13:40
  • The fact is: doesn't exist the true "immutability". You can choose between List which is readonly or MutableList which is mutable. So if you really want something immutable you might look at Guava (https://github.com/google/guava) – mugx Dec 10 '17 at 13:43
  • @AndreaMugnaini - let me rephrase my question. Do you know if it is possible in Kotlin to have a data class with primary constructor and property with the same name as the name of the primary constructor argument. And when the object is created to initialise the property using the argument in the constructor and some custom code? – plamkata__ Dec 10 '17 at 13:50
  • 1
    Let's say 99% satisfying. Thanks for the help. – plamkata__ Dec 10 '17 at 14:02