目录
1.Filed
2.初始化?
2.1.主构造函数
?2.2.在主构造函数里定义属性
2.3.次构造函数
2.4.延迟初始化
2.5. 惰性初始化
2.6初始化陷阱?
3.继承
3.1.关键字open
3.2.子类转父类用关键字as
3.3.关键字:object
? ? ? ? 3.3.1、单例?
? ? ? ? 2、匿名内部类?
? ? ? ? 3、伴生对象
1.Filed
????????针对你定义的每一个属性,Kotlin都会产生一个filed、getter和setter。你不能直接定义field,kotlin封装号了field,保护它的数据,只暴露了getter和setter使用。只有可变属性才会有setter方法。但在需要控制如何读写属性数据时,你也可以自定义它们。需要注意的是,在java中没有给field定义访问模式时,默认的是public。而kotlin,没有定义访问模式时,默认的是private。
class Player {
var name = "abc"
}
fun main() {
val player = Player()
println(player.name)
}
????????这并不能看得出咱们上边的说法,我们来看看字节码中是怎样给的。
public final void main() {
FiledInstanceExtendsTest.Player player = new FiledInstanceExtendsTest.Player();
boolean var2 = false;
System.out.println(player);
}
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"\u0000\u0014\n\u0002........"},
d2 = {"Lcom/example/kotlinstudy/FiledInstanceExtendsTest$Player;", "", "()V", "name", "", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "kotlinStudy.app"}
)
public static final class Player {
@NotNull
private String name = "abc";
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
}
? ? ? ? 这样就显而易见了,name的访问模式是private,我们声明的时候没有用?号,是NotNull。以及提供了我们getter、setter方法。但如果我们自己给name声明private时,就不能用了。
class Player {
private var name = "abc"
}
fun main() {
val player = Player()
println(player)
}
这里我们声明private,我们看看字节码。
public final void main() {
FiledInstanceExtendsTest.Player player = new FiledInstanceExtendsTest.Player();
boolean var2 = false;
System.out.println(player);
}
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082\u000e¢\u0006\u0002\n\u0000¨\u0006\u0005"},
d2 = {"Lcom/example/kotlinstudy/FiledInstanceExtendsTest$Player;", "", "()V", "name", "", "kotlinStudy.app"}
)
public static final class Player {
private String name = "abc";
}
按照我们刚开始的概念,kotlin帮我们设置好了filed、getter和setter。再根据默认为public(其实你给访问模式为public是一样的),在字节码反编译成java中,却是private。我们就知道了,平常使用的时候不用给访问模式,直接定义即可。如果你给的访问权限是private,就无法在外部直接引用了,像player.name这样使用,只能自己在类里边写一个函数,自行返回了。这样使用方式就跟java类似了,那这样就失去了咱们的优势。代码如下:
class Player {
private var name = "abc"
fun getName():String {
return name;
}
fun setName(name:String) {
this.name = name;
}
}
fun main() {
val player = Player()
player.setName("bbc")
println(player.getName())
}
运行结果:bbc?
? ? ? ? 我们也可以自己实现getter和setter方法。
class Player {
var name = "abc"
get() = field.capitalize()
var age = 10
get() = field.absoluteValue;
}
fun main() {
val player = Player()
player.name = "bbc"
player.age = -50
println("${player.name} + ${player.age}")
}
运行结果:Bbc + 50
? ? ? ? 上边例子只写了get方法,set方法也是一样的,就没有写了。?
2.初始化?
2.1.主构造函数
? ? ? ? 我们在Player1类中定义一个主构造函数,使用临时变量作为Player的各个属性提供初始值,Kotlin中,在()括号中我们可以定义参数,这就是我们的主构造函数。临时变量,通常都以下划线开头来命名,为便于识别。临时变量赋值完就会被回收了!
class Player1(
_name:String,
_age:Int,
_isNormal:Boolean
) {
var name = _name;
var age = _age;
var isNormal = _isNormal
}
fun instanceTest() {
var player1 = Player1("hua", 24, true)
println("name:${player1.name},age:${player1.age},isNormal:${player1.isNormal}")
}
?运行结果:name:hua,age:24,isNormal:true
?2.2.在主构造函数里定义属性
? ? ? ? 咱们还可以更简洁一点,直接在主构造函数中去定义属性。
class Player1(
_name:String,
var age :Int,
var isNormal:Boolean
) {
var name = _name;
}
fun instanceTest() {
var player1 = Player1("hua", 24, true)
println("name:${player1.name},age:${player1.age},isNormal:${player1.isNormal}")
}
运行结果和之前是一样的,这样是不是更方便了,又省了几行代码。
2.3.次构造函数
? ? ? ? 有主就有次,不然没有次也实在是满足不了我们各种各样的需求。
class Player1(
_name:String,
var age :Int,
var isNormal:Boolean
) {
var name = _name;
constructor(name:String) : this(name, 100, false)
}
fun instanceTest() {
var player1 = Player1("hua", 24, true)
var player2 = Player1("zhang")
println("name:${player1.name},age:${player1.age},isNormal:${player1.isNormal}")
println("name:${player2.name},age:${player2.age},isNormal:${player2.isNormal}")
}
运行结果:name:hua,age:24,isNormal:true ????????????????name:zhang,age:100,isNormal:false
2.4.延迟初始化
? ? ? ? 延迟初始化可以在声明时不直接初始化,放在后边使用时初始化。关键字:lateinit
//延迟初始化
class Player9(var name: String) {
lateinit var age:Any
fun findAge() {
age = 5
}
fun finish() {
if (::age.isInitialized) println(age)
}
}
fun lateInitTest() {
val player = Player9("hua")
player.findAge()
player.finish()
}
运行结果: 5
? ? ? ? 我们lateinit定义了age,然后在findAge函数中初始化了它。最后在finish中判断age是否初始化,初始化了才输出它。如果我们注释player.findAge()那么运行结果就什么都没有输出了。?
2.5. 惰性初始化
? ? ? ? 延迟初始化不是唯一的推后初始化方式,你也可以暂时不初始化某个变量,直到首次使用它,再初始化它,这叫惰性初始化。使用方式:val name by lazy{.....}
//惰性初始化
class Player10(var name: String) {
val age by lazy { 10 }
}
fun lazyTest() {
val player = Player10("hua")
println("${player.name} + ${player.age}")
}
?初始化:hua + 10
2.6初始化陷阱?
? ? ? ? 一:声明变量应该放在初始化块(init{....})之前。
? ? ? ? 二:一个属性放在初始化块里进行初始化,但其他函数中又直接使用该属性,会报空指针。
? ? ? ? 三:编译器看到所有属性都初始化了,所以代码编译没问题,但运行结果为null
还有初始化顺序,这个看字节码就能明白理解了。哈哈哈,懒得写上来了
3.继承
3.1.关键字open
? ? ? ? 父类要有open关键字,才可以被继承。 ? ? ? ? 父类函数要有open关键字,才可以被子类重载。
? ? ? ? 从字节码上看,不写open关键字,类和函数都被final定义了,所以无法被继承和重载。
//继承
open class Product(val productName:String) {
open fun printlnProductName() = "println $productName"
fun notOpenFun() {
println(productName)
}
}
class Car : Product("Car") {
override fun printlnProductName(): String {
return "println $productName xxx"
}
}
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"..."},
d2 = {"....."}
)
public static class Product {
@NotNull
private final String productName;
@NotNull
public String printlnProductName() {
return "println " + this.productName;
}
public final void notOpenFun() {
String var1 = this.productName;
boolean var2 = false;
System.out.println(var1);
}
.....
}
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"....."},
d2 = {.....}
)
public static final class Car extends FiledInstanceExtendsTest.Product {
@NotNull
public String printlnProductName() {
return "println " + this.getProductName() + " xxx";
}
.....
}
????????看到转换成java的大家应该就很明了了,car被final修饰,product被open关键字修饰,所以没有被final修饰。函数也是这样如此。
3.2.子类转父类用关键字as
car as Product
????????kotlin有类型推断,如果一个函数需要父类,你直接传子类就行,也可以不用转成父类。java的话就要(Product)car转换成父类传进去了。
3.3.关键字:object
? ? ? ? 关键字object用处挺多。1、用于单例。2、匿名内部类。3、伴生对象
? ? ? ? 3.3.1、单例?
//单例
object SingleUtil {
init {
println("loading instance")
}
fun getPiuPiu() {
println("piupiu")
}
}
fun singleTest() {
SingleUtil.getPiuPiu()
SingleUtil.getPiuPiu()
}
运行结果:loading instance ????????????????piupiu ????????????????piupiu
? ? ? ? 2、匿名内部类?
//匿名内部类
open class Player11(var name:String) {
open fun meilidepaomo() {
println("++++++$name")
}
}
fun noNameTest() {
var lala = object : Player11("huahua") {
override fun meilidepaomo() {
println("------$name")
}
}
lala.meilidepaomo()
}
运行结果:------huahua
????????匿名内部类用的比较多,经常自定义Listner,或者回调。只用一次就丢了,
? ? ? ? 3、伴生对象
? ? ? ? 如果你想将某个对象的初始化和一个类的实例捆绑在一起,就可以考虑伴生对象,使用companion修饰符,一个类里只能有一个伴生对象。
//伴生对象
class companionObject() {
companion object {
private final const val URL = "www.baidu.com"
fun openUrl() {
println("正在打开URL:$URL")
}
}
}
伴生对象companion object 只初始化一次,用的时候才会加载内部资源。?
|