嗨,我是Kotlin世界的新手。我喜欢到目前为止所看到的,并开始考虑将我们在Java应用程序中使用的一些库转换为Kotlin。
这些库充满了带有setter,getter和Builder类的Pojos。现在我用谷歌搜索了什么是在Kotlin中实施Builders的最佳方法,但没有成功。
第二次更新:问题是如何使用Kotlin中的一些参数为简单的pojo编写Builder设计模式?下面的代码是我尝试编写java代码然后使用eclipse-kotlin-plugin转换为Kotlin。
class Car private constructor(builder:Car.Builder) {
var model:String? = null
var year:Int = 0
init {
this.model = builder.model
this.year = builder.year
}
companion object Builder {
var model:String? = null
private set
var year:Int = 0
private set
fun model(model:String):Builder {
this.model = model
return this
}
fun year(year:Int):Builder {
this.year = year
return this
}
fun build():Car {
val car = Car(this)
return car
}
}
}
答案 0 :(得分:214)
首先,在大多数情况下,您不需要在Kotlin中使用构建器,因为我们有默认和命名参数。这使您可以编写
class Car(val model: String? = null, val year: Int = 0)
并像这样使用它:
val car = Car(model = "X")
如果您绝对想要使用构建器,请按以下步骤操作:
使构建器成为companion object
没有意义,因为object
是单例。而是将其声明为嵌套类(默认情况下在Kotlin中是静态的)。
将属性移动到构造函数中,以便也可以以常规方式实例化对象(如果构造函数不应该为构造函数,则使用私有构造函数)并使用构造函数将构建器和委托作为主构造函数。代码如下:
class Car( //add private constructor if necessary
val model: String?,
val year: Int
) {
private constructor(builder: Builder) : this(builder.model, builder.year)
class Builder {
var model: String? = null
private set
var year: Int = 0
private set
fun model(model: String) = apply { this.model = model }
fun year(year: Int) = apply { this.year = year }
fun build() = Car(this)
}
}
用法:val car = Car.Builder().model("X").build()
此代码可以使用builder DSL:
进一步缩短class Car (
val model: String?,
val year: Int
) {
private constructor(builder: Builder) : this(builder.model, builder.year)
companion object {
inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
}
class Builder {
var model: String? = null
var year: Int = 0
fun build() = Car(this)
}
}
用法:val car = Car.build { model = "X" }
如果某些值是必需的并且没有默认值,则需要将它们放在构建器的构造函数中以及我们刚定义的build
方法中:
class Car (
val model: String?,
val year: Int,
val required: String
) {
private constructor(builder: Builder) : this(builder.model, builder.year, builder.required)
companion object {
inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build()
}
class Builder(
val required: String
) {
var model: String? = null
var year: Int = 0
fun build() = Car(this)
}
}
用法:val car = Car.build(required = "requiredValue") { model = "X" }
答案 1 :(得分:10)
因为我使用Jackson库来解析JSON中的对象,所以我需要一个空构造函数,而且我不能有可选字段。所有字段都必须是可变的。然后我可以使用这个与Builder模式相同的漂亮语法:
val car = Car().apply{ model = "Ford"; year = 2000 }
答案 2 :(得分:7)
我个人从未见过Kotlin的建筑师,但也许只是我。
所有验证都需要在init
块中发生:
class Car(val model: String,
val year: Int = 2000) {
init {
if(year < 1900) throw Exception("...")
}
}
在这里,我冒昧地猜测你真的不希望model
和year
变化。此外,这些默认值似乎毫无意义(特别是null
name
),但我留下了一个用于演示目的。
意见: Java中使用的构建器模式作为没有命名参数的生存方式。在具有命名参数的语言(如Kotlin或Python)中,使用具有长列表(可选)参数的构造函数是一种很好的做法。
答案 3 :(得分:4)
我见过许多例子,宣称额外的乐趣是建设者。我个人喜欢这种方法。省力写建设者。
package android.zeroarst.lab.koltinlab
import kotlin.properties.Delegates
class Lab {
companion object {
@JvmStatic fun main(args: Array<String>) {
val roy = Person {
name = "Roy"
age = 33
height = 173
single = true
car {
brand = "Tesla"
model = "Model X"
year = 2017
}
car {
brand = "Tesla"
model = "Model S"
year = 2018
}
}
println(roy)
}
class Person() {
constructor(init: Person.() -> Unit) : this() {
this.init()
}
var name: String by Delegates.notNull()
var age: Int by Delegates.notNull()
var height: Int by Delegates.notNull()
var single: Boolean by Delegates.notNull()
val cars: MutableList<Car> by lazy { arrayListOf<Car>() }
override fun toString(): String {
return "name=$name, age=$age, " +
"height=$height, " +
"single=${when (single) {
true -> "looking for a girl friend T___T"
false -> "Happy!!"
}}\nCars: $cars"
}
}
class Car() {
var brand: String by Delegates.notNull()
var model: String by Delegates.notNull()
var year: Int by Delegates.notNull()
override fun toString(): String {
return "(brand=$brand, model=$model, year=$year)"
}
}
fun Person.car(init: Car.() -> Unit): Unit {
cars.add(Car().apply(init))
}
}
}
我还没有找到一种方法可以强制某些字段在DSL中初始化,就像显示错误而不是抛出异常一样。如果有人知道,请告诉我。
答案 4 :(得分:4)
一种方法是执行以下操作:
class Car(
val model: String?,
val color: String?,
val type: String?) {
data class Builder(
var model: String? = null,
var color: String? = null,
var type: String? = null) {
fun model(model: String) = apply { this.model = model }
fun color(color: String) = apply { this.color = color }
fun type(type: String) = apply { this.type = type }
fun build() = Car(model, color, type)
}
}
使用示例:
val car = Car.Builder()
.model("Ford Focus")
.color("Black")
.type("Type")
.build()
答案 5 :(得分:1)
对于一个简单的课程,你不需要一个单独的建设者。你可以像Kirill Rakhman所描述的那样使用可选的构造函数参数。
如果你有更复杂的类,那么Kotlin提供了一种创建Groovy样式的Builders / DSL的方法:
以下是一个例子:
答案 6 :(得分:0)
我想说Kotlin中的模式和实现几乎保持不变。由于默认值,您有时可以跳过它,但是对于更复杂的对象创建,构建器仍然是一个无法忽略的有用工具。
答案 7 :(得分:0)
你可以在kotlin中使用可选参数 例如:
fun myFunc(p1: String, p2: Int = -1, p3: Long = -1, p4: String = "default") {
System.out.printf("parameter %s %d %d %s\n", p1, p2, p3, p4)
}
然后
myFunc("a")
myFunc("a", 1)
myFunc("a", 1, 2)
myFunc("a", 1, 2, "b")
答案 8 :(得分:0)
class Foo private constructor(@DrawableRes requiredImageRes: Int, optionalTitle: String?) {
@DrawableRes
@get:DrawableRes
val requiredImageRes: Int
val optionalTitle: String?
init {
this.requiredImageRes = requiredImageRes
this.requiredImageRes = optionalTitle
}
class Builder {
@DrawableRes
private var requiredImageRes: Int = -1
private var optionalTitle: String? = null
fun requiredImageRes(@DrawableRes imageRes: Int): Builder {
this.intent = intent
return this
}
fun optionalTitle(title: String): Builder {
this.optionalTitle = title
return this
}
fun build(): Foo {
if(requiredImageRes == -1) {
throw IllegalStateException("No image res provided")
}
return Foo(this.requiredImageRes, this.optionalTitle)
}
}
}
答案 9 :(得分:0)
当今人们应该检查科特林的Type-Safe Builders。
使用所说的对象创建方式看起来像这样:
html {
head {
title {+"XML encoding with Kotlin"}
}
// ...
}
vaadin-on-kotlin框架是一个很好的“实际应用”用法示例,该框架利用类型安全构建器来构建assemble views and components。
答案 10 :(得分:0)
我在Kotlin中使用以下代码实现了一个基本的Builder模式:
data class DialogMessage(
var title: String = "",
var message: String = ""
) {
class Builder( context: Context){
private var context: Context = context
private var title: String = ""
private var message: String = ""
fun title( title : String) = apply { this.title = title }
fun message( message : String ) = apply { this.message = message }
fun build() = KeyoDialogMessage(
title,
message
)
}
private lateinit var dialog : Dialog
fun show(){
this.dialog= Dialog(context)
.
.
.
dialog.show()
}
fun hide(){
if( this.dialog != null){
this.dialog.dismiss()
}
}
}
最后
Java:
new DialogMessage.Builder( context )
.title("Title")
.message("Message")
.build()
.show();
科特琳:
DialogMessage.Builder( context )
.title("Title")
.message("")
.build()
.show()
答案 11 :(得分:0)
我正在研究一个Kotlin项目,该项目公开了Java客户端使用的API(无法利用Kotlin语言构造)。我们必须添加构建器以使其在Java中可用,因此我创建了一个@Builder批注:https://github.com/ThinkingLogic/kotlin-builder-annotation -基本上可以替代Kotlin的Lombok @Builder注释。
答案 12 :(得分:0)
我迟到了。如果必须在项目中使用Builder模式,我也会遇到同样的难题。后来,经过研究,我意识到这是绝对不必要的,因为Kotlin已经提供了命名参数和默认参数。
如果您确实需要实施,那么Kirill Rakhman的答案是如何以最有效的方式实施的可靠答案。您可能会发现有用的另一件事是https://www.baeldung.com/kotlin-builder-pattern,您可以在Java和Kotlin的实现上进行比较和对比