15

I'm using data classes in Kotlin to significantly reduce the amount of Java code I would otherwise have to write.

However, in one of my Java classes, I'm not sure what to do to achieve the same result in Kotlin.

My Java class looks a bit like this:

public class DataObject {

    private int mId;
    private String mName;

    public DataObject(int id, String name) {
        mId = id;
        mName = name;
    }

    public DataObject(Context context, int id) {
        mId = id;
        Cursor cursor = ...
        cursor.moveToFirst();
        mName = cursor.getString(...);
        cursor.close();
    }

    public int getId() {
        return mId;
    }

    public String getName() {
        return mName;
    }

}

I've tried to rewrite it in Kotlin, and so far I have this:

data class DataObject(val id: Int, val name: String) {

    constructor(context: Context, id: Int) : this(id, fetchName(context))

    private fun fetchName(context: Context): String {
        val cursor = ...
        cursor.moveToFirst()
        val name = cursor.getString(...)
        cursor.close()
        return name
    }

}

But my IDE (Android Studio) is underlining the part where I call fetchName(context) in my constructor in red. It displays the following message:

Cannot access fetchName before superclass constructor has been called

How should I resolve this issue?

Farbod Salamat-Zadeh
  • 19,687
  • 20
  • 75
  • 125

2 Answers2

19

You can only use member functions on a fully constructed objects. One way to work around that is to use private extension function or simply a function to fetch name:

private fun Context.fetchName(): String {
    ///...
    return cursor.getString(1)
}

data class DataObject(val id: Int, val name: String) {
    constructor(context: Context, id: Int) : this(id, context.fetchName())
}

Although I do think that using Cursor is a bit too heavy job for constructor. I'd use separate Factory like so:

data class DataObject(val id: Int, val name: String) {
    object FromCursorFactory {
        fun create(id: Int, context: Context): DataObject {
            val name = context.fetchName()
            return DataObject(id, name)
        }
    }
}

Further reading:

Community
  • 1
  • 1
miensol
  • 39,733
  • 7
  • 116
  • 112
  • Thanks. Just had another quick question. When I put the extension function `Context.fetchName()` in the data class, it still gave me the warning, but when I moved it outside the data class, the warning disappeared. Why is this? – Farbod Salamat-Zadeh Jul 20 '16 at 12:47
  • 3
    When an extension function is in the class it's still a function that has access to all members of the enclosing class instance and thus requires **the instance**. – miensol Jul 20 '16 at 12:56
3

Another approach is to use companion object. This will allow you to call the function outside of the data class as well (maybe not useful in your particular case)

data class DataObject(val id: Int, val name: String) {

  constructor(context: Context, id: Int) : this(id, fetchName(context))

  companion object {

    fun fetchName(context: Context): String {
      val cursor = ...
      ...
      return name
    }
  }
}