属性与field关键字
- kotlin为每个class的变量自动生成get和set方法
- 可以通过自定义get()/set(value)来覆盖自动生成的方法
- 在get/set方法中通过field关键字来获取定义属性的值
- var 转化为 private
- val 转化为 private final
class Test{
var name ="abc"
get() = field.capitalize()
set(value) {
field = value + "a"
}
}
//转化后的代码
public final class Test {
@NotNull
private String name = "abc";
@NotNull
public final String getName() {
return StringsKt.capitalize(this.name);
}
public final void setName(@NotNull String value) {
Intrinsics.checkNotNullParameter(value, "value");
this.name = value + "a";
}
}
构造函数
- 通过(var param) 来定义属性
- 当我们新建对象时,会将属性同步初始化
class PlayB(var name:String){}
var p = PlayB("hello")
//等价于
public final class PlayB {
@NotNull
private String name;
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
public PlayB(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
}
}
普通变量
普通变量只使用一次,通常用下划线开头_
class PlayB(_name:String){}
var p = PlayB("hello") // p无法访问_name,,它仅是一个临时变量
//等价于
public final class PlayB {
public PlayB(@NotNull String _name) {
Intrinsics.checkNotNullParameter(_name, "_name");
super();
}
}
多个构造函数
- 次构造函数中,不允许使用var定义属性
- 可以提供默认参数
class Player(var name:String , var age:Int = 10 , var sex:Boolean){
constructor(name: String):this(name ,100 ,false)
constructor(name: String , age: Int):this(name ,age ,false){
this.name = name.toUpperCase()
}
}
初始化代码块 init
构造函数执行完毕后,执行初始化代码块,一般用于变量或属性的初始化,初始化代码块实际上会被添加到构造方法中顺序执行(请注意顺序二字)
class Player(var name:String , var _age:Int , var sex:Boolean){
constructor(name: String):this(name ,100 ,false)
constructor(name: String , age: Int):this(name ,age ,false){
this.name = name.toUpperCase()
}
init {
println("do something initial $p")
}
val p:String = "father"
}
//输出
/kotlinStudy/src/ClassTest.kt:18:40
Kotlin: Variable 'p' must be initialized
//等价于 , 此处的p由于尚未初始化是获取不到的.
public Player(@NotNull String name, int _age, boolean sex) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this._age = _age;
this.sex = sex;
String var4 = "do something initial " + this.p;
boolean var5 = false;
System.out.println(var4);
this.p = "father";
}
初始化顺序
- 主构造函数申明的属性
- 类级别的属性
- init代码块中的属性
- 次构造函数中的属性
延迟初始化lateinit
::wapeon.isInitialized表示调用初始化检查函数,它是从lateinit的扩展
class PlayerB{
lateinit var wapeon: String
fun ready(){
wapeon = "gun"
}
fun battle(){
if (::wapeon.isInitialized) println(" ready to battle")
}
}
var pb = PlayerB()
pb.ready()
pb.battle()
惰性初始化 懒汉加载
- val config by lazy {xxx}
- 在首次使用的时候执行初始化操作,仅初始化一次
- 不能被设置其它值,因此不能采用var来定义属性,必须是val
class PlayerC{
val config:String by lazy { loadConfig() }
private fun loadConfig():String{
println("load config ")
return "config"
}
}
//如果用var来定义变量,会提示以下错误
Type 'Lazy<TypeVariable(T)>' has no method 'setValue(PlayerC, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property)
继承
- 默认情况下class不可被继承,只有申明为open的class才可被继承
- 类中的方法也需要申明为open才可以被重写,默认情况下方法是public final类型
- 判断对象类型可以用 is
- 所有的类默认继承自 Any超类
open class PlayerC(name:String){
val config:String by lazy { loadConfig() }
open fun loadConfig():String{
println("load config ")
return "config"
}
}
class Child(name:String) : PlayerC(name) {
override fun loadConfig():String{
return "child config"
}
}
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
object 关键字
构造单例对象
- 申明这样的对象时,不需要加class修饰
- 它不能提供构造函数
- 是一个线程安全的单例,类的初始化锁解决线程竞争问题
- static包含的代码会在Application被加载的时候执行
object Application{
}
//等价于
public final class Application {
@NotNull
public static final Application INSTANCE;
private Application() {
}
static {
Application var0 = new Application();
INSTANCE = var0;
}
}
构造匿名内部类对象
用于创建一个匿名内部类对象,如java中的各种listener
open class Application{
init {
println("init application")
}
open fun doSomething(){
println("do some thing")
}
}
var App = object : Application() {
init {
println("init App")
}
override fun doSomething() {
println("doSomething in App")
}
}
App.doSomething()
App.doSomething()
//转化出来的代码
App = new Application() {
public void doSomething() {
String var1 = "doSomething in App";
boolean var2 = false;
System.out.println(var1);
}
{
String var1 = "init App";
boolean var2 = false;
System.out.println(var1);
}
};
//输出
init application
init App
doSomething in App
doSomething in App
构造伴生对象
- 静态内部单例
- companion object 构造一个伴生对象
- 生成了一个命名为Application.Companion的静态内部类
- 一个类只能有一个伴生对象,因为默认生成的对象和类会重名
- 生成了一个静态对象
public static final Application.Companion Companion - 它适用于初始化一些静态代码块,仅会被初始化一次.
- class Internal也回产生一个静态内部类,它与companion object的区别在于,它不会产生静态对象.
open class Application{
companion object {
val config:String="config"
init {
println("do some initial")
}
fun loadConfig() :String= "load something"
}
class Internal{
fun loadConfig() :String= "load Internal"
}
}
println( Application.loadConfig())
println( Application.loadConfig())
//输出
do some initial
load something
load something
//转化后的java代码
public class Application {
@NotNull
public static final Application.Companion Companion = new Application.Companion((DefaultConstructorMarker)null);
public static final class Internal {
@NotNull
public final String loadConfig() {
return "load Internal";
}
}
public static final class Companion {
@NotNull
public final String loadConfig() {
return "load something";
}
@NotNull
public final String getConfig() {
return Application.config;
}
public final void setConfig(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
Application.config = var1;
}
private Companion() {
}
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
静态内部类
在类中定义的其它类默认都是静态内部类,如Internal是一个
public static final的静态内部类
open class Application{
var appid:String = "1233"
init {
println("init application")
}
open fun doSomething(){
println("do some thing")
}
companion object {
fun loadConfig() :String= "load something"
}
class Internal{
fun internalFun(){
}
}
}
//转化后的代码
public static final class Internal {
}
data 数据类
- 用来存储数据
- 重新实现了toString,hashCode和equals方法
- 增加了copy函数的定义,可以修改某个属性
- == 比较的是引用 .普通对象比较的是引用,也就是必须hashCode相等并且equals方法返回true才表示相等,而data对象通过实现自定义的hashCode和equals方法,使得只要值相等,==就返回true
data class Point(var x:Int , var y:Int){}
var p1 = Point(10 , 20)
var p2 = Point(10 , 20)
var p3 = p2.copy(5)
println(p1 == p2) //true
println(p1) // Point(x=10, y=20)
println(p3) // Point(x=5, y=20)
//等价于
public final class Point {
private int x;
private int y;
public final int getX() {
return this.x;
}
public final void setX(int var1) {
this.x = var1;
}
public final int getY() {
return this.y;
}
public final void setY(int var1) {
this.y = var1;
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public final int component1() {
return this.x;
}
public final int component2() {
return this.y;
}
@NotNull
public final Point copy(int x, int y) {
return new Point(x, y);
}
@NotNull
public String toString() {
return "Point(x=" + this.x + ", y=" + this.y + ")";
}
public int hashCode() {
return Integer.hashCode(this.x) * 31 + Integer.hashCode(this.y);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Point) {
Point var2 = (Point)var1;
if (this.x == var2.x && this.y == var2.y) {
return true;
}
}
return false;
} else {
return true;
}
}
}
解构语法支持
通过申明若干个组件函数component1,component2,....,每个组件函数负责返回一个属性值.数据类自动做了解构组件函数申明
普通对象支持解构
data对象解构自动支持
var p1 = Point(10 , 20)
var (x , y) = p1
data数据类使用条件
- 必须包含至少一个带参数的主构造函数
- 构造参数类型必须是val 或者var
- class不能是abstract , open , sealed 和 inner修饰
Enum枚举
可以传递参数,定义函数.
enum class EnumTest(var x :Int , var y:Int) {
dir_0(10 , 20),
dir_1(100 , 200),
dir_2(1000 , 2000)
}
fun main() {
var e = EnumTest.dir_0
println(e is EnumTest) // true
println("${e.x} ${e.y}") // 10 20
}
运算符重载
data class Point(var x:Int , var y:Int){
operator fun plus(point: Point) : Point{
return Point(x + point.x , y + point.y)
}
//或者
operator fun minus(point: Point) = Point(x - point.x , y - point.y)
}
fun main() {
var p1 = Point(10 , 20)
var p2 = Point(20 , 40)
println(p1 + p2) // Point(x=30, y=60)
println(p1 - p2) // Point(x=-10, y=-20)
}
密封类 sealed class
等价于抽象内部类,外部无法实例化.
sealed class sealClass {
object sealA:sealClass()
object sealB:sealClass()
}
//等价于
public abstract class sealClass {
private sealClass() {
}
public static final class sealA extends sealClass {
@NotNull
public static final sealClass.sealA INSTANCE;
private sealA() {
super((DefaultConstructorMarker)null);
}
static {
sealClass.sealA var0 = new sealClass.sealA();
INSTANCE = var0;
}
}
public static final class sealB extends sealClass {
@NotNull
public static final sealClass.sealB INSTANCE;
private sealB() {
super((DefaultConstructorMarker)null);
}
static {
sealClass.sealB var0 = new sealClass.sealB();
INSTANCE = var0;
}
}
}
它解决了枚举类型的限制(枚举类型必须一致,比如都是Int/String)
sealed class Color {
class Red(val value: Int) : Color()
class Green(val value: Int) : Color()
class Blue(val name: String) : Color()
}
fun isInstance(color: Color) {
when (color) {
is Color.Red -> TODO()
is Color.Green -> TODO()
is Color.Blue -> TODO()
}
}
sealed class 一些用法
不同参数的操作
sealed class UiOp {
object Show: UiOp()
object Hide: UiOp()
class TranslateX(val px: Float): UiOp()
class TranslateY(val px: Float): UiOp()
}
fun execute(view: View, op: UiOp) = when (op) {
UiOp.Show -> view.visibility = View.VISIBLE
UiOp.Hide -> view.visibility = View.GONE
is UiOp.TranslateX -> view.translationX = op.px
is UiOp.TranslateY -> view.translationY = op.px
}