原因:更加简洁的构造方法
你还在为 Java 中重载多个构造方法去初始化一个类而困扰吗?在 Kotlin 中完全不用担心这个问题,Kotin 用合理的设计为我们规避了这些问题。通过 Java 和 kotlin 对比来看看 Kotlin 是如何处理构造函数的。
-
Java 的构造方法
Java 是如何实现多个构造方法的,答案是显而易见的,是通过重载。Student 的实例中就展示了两个区分参数的构造方法。
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name) {
this.name = name;
}
}
这样的实现方式,造成的问题有两个:
- 如果要支持任意参数组合来创建对象的话,需要实现的构造方法会很多。
- 每个构造方法代码其实存在冗余
那么针对以上问题,Kotlin 是如何设计来解决的,是否能够减少构造方法的数量的同时最大程度的复用代码,如果能又该怎么使用呢?
-
Kotlin 的构造方法
首先第一点 kotlin 破天荒的支持了参数设置默认值,通过对参数设置默认值,可以有效的减少构造方法的数量,Java 中通过重载机制实现的构造方法在 Kotlin 中都可以通过设置参数默认值的方法替代。
StudentKotlin 实例中就 对于 参数 name 进行了默认值的设置,有了默认值的参数在对象实例化时可以不赋值,当然在使用时如果想要重新赋值也是可以的,Kotlin 构造方法还有什么其他特点呢?
Kotlin 主构造方法特点
-
由于参数具有默认值的存在,所以在创建对象的时候,最好是可以直接指定名称,否则需要和 java 一样,必须按照参数顺序进行赋值。是的你没听错,只要参数**指定名称就可以不按照参数顺序进行赋值,**当然前提是指定名称。
-
如果某些参数不需要设置默认值,可以通过设置 val 或是 var 修饰符,但在实例化对象时该参数将会被强制要求赋值否则会报错。
-
init 方法中也可以对构造方法中的参数进行赋值,例如 StudentKotlin 实例化时将 age 赋值为 5,init 方法又将 age 赋值为 2,最后结果却是 2 ,这其实和构造方法和 init 的初始化顺序有关,博主以前有写过一篇关于初始化顺序的文章,感兴趣的同学可以翻翻看。
class StudentKotlin(name: String = "xiaoming", var age: Int ) {
init{ age = 2 println("姓名 age) }
}//实例化 studentKotlin1 val studentKotlin1:StudentKotlin = StudentKotlin(name = "xiaohong",age = 1)
//实例化 studentKotlin2 val studentKotlin2:StudentKotlin = StudentKotlin(age = 2,name = "xiaohong")
fun main(args: Array) { //实例化 studentKotlin3 age 参数必传 val studentKotlin3: StudentKotlin = StudentKotlin(age = 5)
原因:主从构造方法的引入
其实 Kotlin 也是支持多个构造方法的,Kotlin 将这种构造方法体系叫做主从构造方法,我们上文提到的构造方法就是主构造方法,在实际开发中,虽然 Kotlin 支持构造参数设置默认值在主构造参数中,但在特殊场景中,其实还是需要子构造方法的参与的,单一的主构造方法还是无法满足我们的需求。
例如有时候我们需要从一个特殊的数据提供者获取构造类的参数值,这时候就凸显出多构造方法的重要性,我们可以通过定义一个额外的构造方法,用来接收这个自定义的参数,这样会方便不少也让程序结构更清晰。
在 Student 例子中,我们有以下需求
我们有已知参数<生日>,我们希望通过已知<生日>直接获取到<年龄>。
-
Java 如何实现?
Java 完全可以通过新增一个构造方法的方式来解决这个问题,添加一个需要参数是 birthday 的构造方法,通过方法获取到年龄并赋值给成员变量
public class Student {
private String name;
private int age;
private int birthday;
public Student(String name) {
this.name = name;
}
public Student(int birthday) {
this.age = getAgeByBirth(birthday);
}
//获取年龄
public int getAgeByBirth(int age) {
...
}
}
-
通过 Kotlin 主从构造实现
其实 Kotlin 也支持多构造方法,和 Java 的区别是,在多个构造方法之间建立了"主从"的关系。通过实例代码我们学习一下主从构造方法的使用
class Student(name: String = "xiaoming", age: Int = 0){
val age:Int = age
//从构造方法
constructor(birth:int):this(getAgeByBirth(birth))
//获取年龄
private fun getAgeByBirth(birth: Int): Int {
...
}
}
主从构造方法的特点
-
通过 constructor 方法定义的构造方法,被称为从构造方法,相应的在类外部定义的构造方法被称为**主构造方法。**当然如果主构造方法存在注解或可见性修饰符,也必须像从构造方法一样加上 constructor 关键字。
internal class StudentKotlin @inject constructor (name: String = "xiaoming", private var age: Int, private val birth: Int) { init{ age = 2 println("姓名 age) } }
-
从构造方法由两部分组成,一部分是其他构造方法的委托,另一部分是由花括号包裹的代码块。执行顺序上会先执行委托的方法,然后再执行自身代码块的逻辑;通过 this 关键字来调用委托的构造方法。如果一类存在主构造方法,那么每个从构造方法都要直接或间接地委托给它。
通过 Java 版的 Android View 实现和 Kotlin 版的 View 实现对比,就可以明白 Kotlin 的构造方法直接和间接委托的概念
Java 版 Android View
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
Kotlin 版 Android View
class MyKotlinView : View {
//构造方法 A
constructor(context: Context?):this(context,null)
//构造方法 B
constructor(context: Context?, attrs: AttributeSet?) : this(context, null,0)
//构造方法 C
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
}
}
Kotlin 版内一共三个构造方法,可以看出 构造方法 A 委托于 构造方法 B,构造方法 B 委托于构造方法 A。
为了表现出拥有主构造方法的情况是怎样的,我们还 可以这样改造。所有的构造方法都是直接或是间接的委托于主构造方法
class MyKotlinView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) :
View(context, attrs, defStyleAttr, defStyleRes) {
constructor(context: Context?) : this(context, null)
constructor(context: Context?, attrs: AttributeSet?) : this(context, null, 0)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(
context,
null,
0,
0
)
}
以上就是本章对于 Java 和 Kotlin 在构造方法上的改进和区别,如果觉得还不错请多多点赞和转发,后续本系列也会分享更多的 Kotlin 和 Java 的干货内容,感谢您的纠错和反馈。
本文参考:《Kotlin核心编程》 --水滴技术团队 感兴趣的同学可以阅读原著。