构造方法
构造方法:类的一种特殊方法,负责新创建对象的初始化工作,为实例变量赋初值。
Java中类的构造方法:
类的构造方法不是要求必须定义的。
① 如果在类中没有定义任何一个构造方法,则 Java 会自动为该类生成一个默认的构造方法(默认构造方法不包含任何参数,且方法体为空);
② 如果在类中显式地定义了一个或多个构造方法,则 Java 不再提供默认构造方法。
★ Kotlin 和 Java 一样,一个类可以声明一个或多个构造方法,但Kotlin的不同点为,区分了主构造方法和从构造方法:
主构造方法:主要而简洁的初始化类的方法,并且在类体外部声明
从构造方法:在类体内部声明
★ 如果没有给类声明任何构造方法,将会生成一个不做任何事情的默认构造方法:
open class Button
★ 大多数情况下,Kotlin 中类的构造方法的声明方式非常简明,要么类名后没有参数(默认构造方法),要么直接把参数与对应属性相关联(主构造方法)。当需要为类定义足够多的构造方法时,使用从构造方法。
一、主构造方法
特点:
声明在类的外部,且一般主构造方法在类头部的类名后面有括号(含参构造方法)
一个类只有一个主构造方法,该主构造方法可以有多个重载参数的构造方法
主构造方法不包含初始化执行语句(因为其放在类首部,所以不能包含初始化执行语句),但是可以将初始化执行语句放在 init 中,为属性赋值
1、写法(在类中声明主构造方法的方式)
声明一个简单类:
class User(val nickname:String)
其中,被括号围起来的语句块就叫作 主构造方法。
括号的目的:表明构造方法的参数,以及定义使用这些参数初始化的属性。
1) 主构造方法写法1:表示方式更为明确的构造方法
-
//代码片段1 表示方式更为明确的代码,nickname是属性
-
class User constructor(_nickname:String){
-
val nickname:String
-
init{//初始化语句块
-
nickname = _nickname
-
}
-
}
constructor关键字 用来开始一个主构造方法或从构造方法的声明;
init关键字 用来引入一个初始化语句块。
初始化语句块:在Kotlin中,主构造方法有语法限制,不能包含初始化代码。 可以在一个类中声明多个初始化语句块。
错误写法:
-
class User constructor(_nickname:String){
-
val nickname = _nickname
-
println("init") //!!!语法错误,因为主构造方法中不能包含类的初始化代码
-
}
正确写法:
-
class User constructor(_nickname:String){
-
val nickname = _nickname
-
init{
-
println("init")
-
}
-
}
2) 主构造方法写法2:去掉constructor关键字
-
//代码片段2 功能和代码片段1相同
-
class User(_nickname:String){
-
val nickname = _nickname //用参数来初始化属性
-
}
初始化执行语句块中的代码可以与nickname属性的声明相结合
主构造方法没有注解或可见性修饰符,可去掉constructor关键字;当有修饰符时,constructor关键字位于修饰符后面
3) 主构造方法写法3:将val关键字加在方法的参数前
-
//代码片段3 功能和代码片段1和2相同 最简洁
-
class User(val nickname:String)
将val关键字加在参数前:属性使用构造方法参数来初始化,这种方式可以替换类中的属性定义
省略花括号:如果类不包含其他操作函数,可省略花括号
2 、为构造方法声明默认值(构造方法的重载)
1) 在 Kotlin 中调用构造方法(使用 Kotlin 声明类)
-
//Kotlin
-
class User(val name:String = "zhangsan", val age:Int = 9)
该类的参数设置了默认值,在Kotlin中默认生成了四个构造方法:
-
User() //name="zhangsan",age=9 注意:这个不是默认构造方法,默认构造方法不包含任何参数,且方法体为空。
-
User(String name) //age=9
-
User(Int age) //name="zhangsan",调用该方法时,需命名参数age
-
User(String name, Int age)
-
//Kotlin
-
fun main(args:Array<String>){
-
val user = User()
-
//name::zhangsan::age::9
-
println("name::" + user.name + "::age::" + user.age)
-
val user2 = User("lisi")
-
//name::lisi::age::9
-
println("name::" + user2.name + "::age::" + user2.age)
-
val user3 = User(age=18)
-
//name::zhangsan::age::18
-
println("name::" + user3.name + "::age::" + user3.age)
-
val user4 = User(name="wangwu", age=18)
-
//name::wangwu::age::18
-
println("name::" + user4.name + "::age::" + user4.age)
-
}
2) 在Java中调用构造方法(使用 Kotlin 声明类):
//Kotlin
class User(val name:String = "zhangsan", val age:Int = 9)
该类的声明方式,在Java中默认生成了两个构造方法:
-
User() //name="zhangsan",age=9 注意:这个不是默认构造方法,默认构造方法不包含任何参数,且方法体为空
-
User(String name, Int age) //name="zhangsan",age=9
-
//Java
-
class Test11{
-
public static void main(String args[]){
-
User user = new User();
-
//name::zhangsan::age::9
-
System.out.println("name::" + user.getName() + "::age::" + user.getAge());
-
User user2 = new User("lisi", 11);
-
//name::lisi::age::11
-
System.out.println("name::" + user2.getName() + "::age::" + user2.getAge());
-
}
-
}
Java中没有函数参数默认值的概念,当从 Java 代码中调用 Kotlin 代码中的函数时,需要显式地指定所有的参数值。当对 Kotlin 中的函数使用 @JvmOverloads 注解 时,编译器会生成 Java 重载函数(从最后一个开始 省略有默认值的参数,构成重载函数的参数)。
-
//Kotlin
-
class User @JvmOverloads constructor(val name:String = "zhangsan", val age:Int=9)
这种类的声明方式,在Java中默认生成了三个构造函数:
-
User() //name="zhangsan",age=9 注意:这个不是默认构造方法,默认构造方法不包含任何参数,且方法体为空
-
User(String name) //age=9
-
User(String name, Int age) //name="zhangsan",age=9
-
//Java
-
class Test11{
-
public static void main(String args[]){
-
User user = new User();
-
//name::zhangsan::age::9
-
System.out.println("name::" + user.getName() + "::age::" + user.getAge());
-
User user2 = new User("lisi", 11);
-
//name::lisi::age::11
-
System.out.println("name::" + user2.getName() + "::age::" + user2.getAge());
-
//Test33Kt.User类中增加了 @JvmOverloads 注解后,增加了重载的构造函数 User(String name)
-
User user3 = new User("lisi");
-
//name::lisi::age::9
-
System.out.println("name::" + user3.getName() + "::age::" + user3.getAge());
-
}
-
}
二、从构造方法(次构造方法)
特点:
声明在类的内部
一个类可声明任意多个从构造方法,通过定义多个从构造函数来配置不同的参数组合
从构造方法可以包含初始化代码块
从构造方法需委托给主构造方法(主从同时存在)
1、写法(声明从构造方法)
-
class User{
-
private val username: String
-
private var age: Int
-
constructor(_username: String, _age: Int){
-
this.username = _username.toUpperCase()
-
this.age = _age
-
}
-
}
注意:
声明从构造方法类似于在 Java 中声明构造方法的方式,区别在于这里使用 constructor 关键字取代了类名
在从构造方法中,不能在 constructor 参数中通过 val 或 var 来为类添加相应的属性
2、委托
1) 主从构造方法同时存在时,从构造方法会直接或者间接调用/委托主构造方法。
在从构造方法存在委托情况时,会先执行委托的方法,然后执行自身。
例1:
-
open class B(){
-
init{
-
println("--B主构造--")
-
}
-
}
-
class A(): B() {
-
init{
-
println("--A主构造--")
-
}
-
constructor(name: String):this() {
-
println("--A次构造--")
-
}
-
}
-
fun main(args:Array<String>){
-
val a2 = A("zhangsan")
-
}
执行结果:
--B主构造--
--A主构造--
--A次构造--
例2:
-
class User(val name: String = "Bob") {
-
init{
-
println("--User主构造--")
-
}
-
constructor(id: Long) : this(){
-
println("--User次构造11111--")
-
}
-
constructor(id: Long, email: String):this() {
-
println("--User次构造22222--")
-
}
-
}
-
fun main(args:Array<String>){
-
val user = User(1,"aoeiuv")
-
println(user.name)
-
}
执行结果:
--User主构造--
--User次构造22222--
以下考虑存在继承关系的时候:
2) 如果子类中没有主构造方法,有从构造方法,且父类没有无参构造方法,那么必须要在子类的从构造方法中调用 super 关键字 委托给父类,来初始化父类,初始化父类时,可调用父类的不同的构造函数
-
// open 标识该类可以被继承
-
open class Mouse {
-
var _name:String = ""
-
var _price:Int = 0
-
constructor(name: String) : this(name, 0){
-
this._name = name;
-
println("--Mouse次构造(含1个参数)--")
-
}
-
constructor(name: String, price: Int) {
-
this._name = name
-
this._price = price
-
println("--Mouse次构造(含2个参数)--")
-
}
-
fun printInfo() {
-
println("name = $_name , price = $_price")
-
}
-
}
-
class AppleMouse: Mouse {
-
constructor(name:String, price:Int):super(name, price){
-
println("--AppleMouse次构造(含2个参数)--")
-
}
-
}
-
fun main(args:Array<String>){
-
val mouse = AppleMouse("luoji",190)
-
mouse.printInfo()
-
}
执行结果:
--Mouse次构造(含2个参数)--
--AppleMouse次构造(含2个参数)--
name = luoji , price = 190
3) 如果子类中没有主构造方法,有从构造方法,但父类有无参构造方法,子类在继承该基类时,无需在从构造方法中使用super关键字,执行时会实现该基类的无参构造器
-
// open 标识该类可以被继承
-
open class Mouse{
-
init {
-
println("--Mouse无参主构造--")
-
}
-
}
-
class LuoMouse: Mouse{
-
constructor(name:String){ //无需使用super关键字
-
println("--LuoMouse无参次构造--")
-
}
-
}
-
fun main(args:Array<String>){
-
LuoMouse("hh")
-
}
执行结果:
--Mouse无参主构造--
--LuoMouse无参次构造--
4) 如果子类中有主构造方法,无论父类什么情况,子类的主构造方法需调用父类的构造方法
-
// open 标识该类可以被继承
-
open class Mouse{
-
constructor(name:String)
-
}
-
class LuoMouse(): Mouse("bob"){
-
constructor(name:String){
-
println("--LuoMouse无参次构造--")
-
}
-
}
-
// open 标识该类可以被继承
-
open class Mouse{
-
constructor()
-
}
-
class LuoMouse(): Mouse(){
-
constructor(name:String){
-
println("--LuoMouse无参次构造--")
-
}
-
}
3、使用场景
1)构造函数的相互调用
比如,需要重写 AppCompatButton 类中的多个构造器,那么就需要用到从构造方法:
-
class MyButton : AppCompatButton {
-
constructor(context: Context) : this(context, null)
-
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.buttonStyle)
-
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
-
}
2)使用不同的参数列表创建类的实例
三、默认构造方法
1、写法
open class Button
2、继承只有一个默认构造方法的类
如果继承了Button类(只有一个默认构造方法),并且子类没有提供任何构造方法,必须显式地调用父类的构造方法(父类名称后面加上一个空括号):
class RadioButton:Button()