5

Regarding data classes it is forbidden to not use var or val keywords in the primary constructor, i.e. every parameter is implicitly turned into a class property. However, sometimes there are cases which I don't want each parameter to be turned into a class property.

So, as far as I can see, there is no chance of passing a parameter in a primary constructor that is accessible only within the constructor and is forgotten after the construction of the instance has finished. Is there a good reason for this?

The only way I see to get around this, is not to use data classes or to use a secondary constructor that allows for non-var/val-prefixed variables. However, having a lot of parameters that need to be passed, a secondary constructor would immensely inflate the class. Of course, I could wrap all the parameters into another object, but that would just kind of shift the problem to another place.

Is there a recommended approach or pattern in order to cope with that?

4

2 回答 2

4

You are not limited at all, you just have to do things a bit differently.

Data classes are intended to be very clear about what they contain and in what order, and only allow members in the primary constructor parameter list.

But you have other options: use a secondary constructor, and/or create top-level functions with the same name as the class with different overloads, or create factory methods in the companion object:

data class Person(val name: String, val age: Int) {
    // secondary constructor
    constructor (name: String): this(name, 0) {
       // ... make a newborn
    }

    // factory methods in companion object
    companion object {
        fun of(name: String, birthdate: LocalDate): Person {
            return Person(name, yearsSince(birthdate))
        }
    }
}

// function with same name as class acting like a constructor
fun Person(name: String, birthdate: LocalDate): Person {
    return Person(name, yearsSince(birthdate))
}

// these all work now:

Person("Fred", 30)                                  // primary constructor
Person("Baby")                                      // secondary constructor
Person("Geoff", LocalDate.parse("12/08/1990"))      // top-level function
Person.of("Jennifer", LocalDate.parse("01/01/1981") // companion function

You can also hide the primary constructor by making it private, but you cannot hide the copy version of that constructor.

By the way, having data classes with this contract for the primary constructor really help serialization/deserialization libraries know what to do with the class that would be guesswork otherwise. It is a good thing!

于 2018-07-29T15:17:05.593 回答
0

我必须说的第一件事是,这是我的个人观点,所以请持保留态度。

来自官方 kotlin文档

我们经常创建主要目的是保存数据的类。在这样的类中,一些标准功能和实用功能通常可以从数据中机械地推导出来。

所以data classes应该被用作数据持有者,它们不应该包含太多逻辑。

从我的角度来看,当您想将某些内容传递给构造函数但类不存储该数据时,可能存在一些与之相关的逻辑。

您想要执行此操作的常见情况是:

  1. 使用一些标志来改变构造函数的行为

  2. 传递一些包含所有所需数据的类,然后将其提取到每个单独的字段。

在第一种情况下,我们清楚地看到这不是data class用例的一部分。

第二种情况只是糟糕的代码,它引入了对另一个类的不必要依赖,并隐藏了该类实际需要的内容。

构造函数应该很简单,它们获取类所需的数据并将其绑定到字段,那里不应该存在太多逻辑,应该由那些使用构造函数来准备所有数据的人决定,如果在创建新实例时有一些可重复的代码,那么用它factory method来封装它可能是个好主意。

于 2018-07-29T14:17:56.187 回答