类的构造器
基本写法
class Person constructor(var age: Int, var name: String)
这个constructor可以省略
class Person(var age: Int, var name: String)
我们这个参数使用var或者val修饰,比如age,它即表示构造器参数,也同时相当于我们定义了一个age成员属性,如果把var或者val去掉,就表示是一个普通的构造参数.用var或者val修饰的属性类内全局可见,如果前面还有public的话,类外也可见,普通的参数只有构造器内可见或者init代码块可见,这个init代码块有点像java中的构造器
class Person(var age: Int, name: String) {
var name: String
init {
this.name = name
}
init {
this.age = 18
}
init {
this.name = "Hello"
}
}
init代码块最终会合并执行
我们再看下上面代码,我们定义了一个属性name,如果我们没有在init中初始化,编译器就会报错
就是说在
Kotlin中属性必须初始化,在Java中一般属性我们没有初始化的话都会有默认值,但是在Kotlin中需要我们手动初始化值,我们先给它默认个null,我们看下
还是报错,因为我们没有声明
name是一个可空类型,如果你一定要给它赋值为null,加个可空就行
class Person(var age: Int, name: String) {
var name: String? = null
}
但是你这样做的话你每次在使用这个name的时候都需要注意下,因为它可空.
接下来我们看下类实现继承
abstract class Animal
class Person(var age: Int, name: String) : Animal()
继承我们之前说过,使用:,同时调用父类构造器,父类默认构造器这里直接使用()即可
我们还知道我们在类上面声明的构造器是主构造器,我们还可以声明副构造器
abstract class Animal
class Person(var age: Int, name: String) : Animal() {
//副构造器
constructor(age: Int) : this(age, "Hello"){
}
}
这里我们注意下,我们一般建议声明主构造器加默认参数,不声明也不报错,类似Java弄多个构造方法一样
除了使用构造器构造一个类,还可以使用工厂函数
val persons = HashMap<String, Person>()
fun Person(name: String): Person {
return persons[name] ?: Person(1, name).also { persons[name] = it }
}
采用了这种写法的就是String
//调用构造器
val str = String()
//使用工厂函数
val str1 = String(charArrayOf('1', '2'))
使用的
Java的String构造方法
类与成员的可见性
可见性对比
| 可见性类型 | Java | Kotlin |
|---|---|---|
| public | 公开 | 与Java相同,默认 |
| internal | x | 模块内可见 |
| default | 包内可见,默认 | x |
| protected | 包内及子类可见 | 类内及子类可见 |
| private | 类内可见 | 类或文件内可见 |
这里的模块一般认为是jar包或者aar包
主要说下internal和default
- 这俩一般是编写
SDK或者公共组件用的比较多,隐藏内部实现细节 default可以通过外部创建相同的包名访问,访问控制很弱default会导致不同层次的类合到一个包下internal可方便处理内外隔离,提升模块代码内聚,减少接口暴露internal修饰的类或者成员在Java中可直接访问
举例说明,假设我们有个Api类提供两个方法,这两个方法又是另外两个类提供的,我们应该怎么写呢?我们的前提是外部不允许直接调用那两个类和对应的方法,只能通过我们这个Api获得,我们先看下Java实现
public class ApiJava {
private CoreApiJavaA coreApiJavaA = new CoreApiJavaA();
private CoreApiJavaB coreApiJavaB = new CoreApiJavaB();
public void a(){
coreApiJavaA.a();
}
public void b(){
coreApiJavaB.b();
}
}
public class CoreApiJavaA {
public void a(){
}
}
public class CoreApiJavaB {
public void b(){
}
}
这样肯定不行对吧,使用public修饰的类外部可以直接访问,所以我们需要去掉public修饰
我们发现我们改完之后会报错,因为现在这个
CoreApiJavaA和我们的ApiJava没在一个包下
所以我们还需要把这两个类从core包内移出来,和ApiJava一个包下,这样就没问题了
如果说ApiJava里面需要多个实现方法,就需要把那些类全部挪到这个包下,这会让代码看起来不容易看
Kotlin实现就很容易,定义成internal就可以
class ApiKotlin {
private val coreApiKotlinA = CoreApiKotlinA()
private val coreApiKotlinB = CoreApiKotlinB()
fun a(){
coreApiKotlinA.a()
}
fun b(){
coreApiKotlinB.b()
}
}
internal class CoreApiKotlinA {
internal fun a(){
}
}
internal class CoreApiKotlinB {
internal fun b(){
}
}
他们也不需要在一个包下面
但是呢,如果仅定义成这样,在Java代码中还是可以访问到的
直接编译运行也不会运行,因为Java哪认得你这个internal,那这个怎么解决呢?就需要我们另辟蹊径了,我们可以在我们的方法上加上注解
internal class CoreApiKotlinA {
@JvmName("%abc")
internal fun a(){
}
}
调用处就会变成
因为%abc在java中不合法,所以不能直接调用
同样我们也可以给我们的属性,构造器,set方法加修饰符
class Foo private constructor(var age: Int)
class Foo1(private var age: Int)
class Foo2 {
private var age: Int = 0
}
class Foo3 {
var age: Int = 0
//set的可见性不能大于属性
private set
//不可以,get的可见性必须与属性一致
//private get
}
类属性的延迟初始化
第一种:可空类型
private var textView : TextView? = null
override fun onCreate(...){
textView = findViewById(R.id.text_view)
textView?.text="Jack"
}
第二种:使用lateinit
private lateinit var textView : TextView
override fun onCreate(...){
textView = findViewById(R.id.text_view)
textView.text="Jack"
}
lateinit会让编译器忽略变量初始化,不支持Int等基础类型- 一般是确定变量生命周期使用
- 复杂的逻辑中不要使用
第三种:使用lazy (推荐使用!!!)
private var textView by lazy {
textView = findViewById(R.id.text_view)
}
代理
- 接口代理:对象X代替当前类A实现接口B的方法
- 属性代理:对象X代替属性a实现
getter/setter方法
代理的作用主要就是简化代码编写,使用by关键子
接口代理:
首先定义一个接口
interface Api {
fun a()
fun b()
fun c()
}
然后我们写一个实现类
class ApiImpl : Api {
override fun a() {}
override fun b() {}
override fun c() {}
}
然后我们想在这个基础上增强,包装这个实现类
class ApiWrapper(val api: Api) : Api {
override fun a() {
api.a()
}
override fun b() {
api.b()
}
override fun c() {
println("调用c方法")
api.c()
}
}
我们就想改写c方法,但是我们却必须复写a和b,怎么样简化呢?我们就可以使用代理
class ApiWrapper(val api: Api) : Api by api {
override fun c() {
println("调用c方法")
api.c()
}
}
这个就是对象api代替类ApiWrapper实现接口Api,对象api的要求就是实现Api接口
属性代理
比如上面我们说过的lazy
class Person(val name: String){
val firstName by lazy {
name.split(" ")[0]
}
}
这个lazy相当于代理firstName的getter
class Foo {
val x: Int by X()
var y: Int by X()
}
class X {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return 2
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, i: Int) {
}
}
代理val需要getValue,代理var还需要setValue,这两个方法的参数就这么写就行
使用案例:使用属性代理读写Properties
思路:
1.创建一个类,拿到所有的Properties,同时提供get/set方法
2.创建一个接口
3.创建一个实现类
4.使用
1.第一步
class PropertiesDelegate(private val path: String, private val defaultValue: String = ""){
private lateinit var url: URL
//获取所有的properties
private val properties: Properties by lazy {
val prop = Properties()
url = try {
javaClass.getResourceAsStream(path).use {
prop.load(it)
}
javaClass.getResource(path)
} catch (e: Exception) {
try {
ClassLoader.getSystemClassLoader().getResourceAsStream(path).use {
prop.load(it)
}
ClassLoader.getSystemClassLoader().getResource(path)!!
} catch (e: Exception) {
FileInputStream(path).use {
prop.load(it)
}
URL("file://${File(path).canonicalPath}")
}
}
prop
}
//get方法
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return properties.getProperty(property.name, defaultValue)
}
//set方法
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
properties.setProperty(property.name, value)
File(url.toURI()).outputStream().use {
properties.store(it, "Hello!!")
}
}
}
第二步:
abstract class AbsProperties(path: String){
protected val prop = PropertiesDelegate(path)
}
第三步:
class Config: AbsProperties("Config.properties"){
var author by prop
var version by prop
var desc by prop
}
第四步:
fun main() {
val config = Config()
println(config.author)
config.author = "Greathfs"
println(config.author)
}
单例object
Java饿汉式单例写法
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
}
Kotlin写法
object Singleton{
}
访问object成员
object Singleton {
var x: Int = 2
fun y() {}
}
Singleton.x
Singleton.y()
静态成员@JvmStatic和@JvmField
@JvmField var x: Int = 2
@JvmStatic fun y(){ }
普通类的静态成员
class Foo {
companion object {
@JvmStatic fun y(){ }
}
}
等价的Java代码
public class Foo {
public static void y(){ }
}
内部类
Java
public class Outer {
class Inner { }
//静态内部类需要加static
static class StaticInner { }
}
Kotlin
class Outer {
//费静态内部类需要加inner
inner class Inner
class StaticInner
}
//用法
val inner = Outer().Inner()
val staticInner = Outer.StaticInner()
匿名内部类
Java
new Runnable() {
@Override
public void run() {
}
};
Kotlin
//可以有多个接口
object : Cloneable, Runnable {
override fun run() {
}
}
数据类 data class
就是普通类前面添加data关键字
data class Book(val id: Long,
val name: String,
val author: Person) {
}
data class Person(val id: Long, val name: String, val age: Int)
这个和我们java中的JavaBean不太一样,这个类不能被继承,不能有无参构造,同时还提供了component,我们反编译下这个类,看下这个类最终生成什么样
public final class Person {
private final long id;
@NotNull
private final String name;
private final int age;
public final long getId() {
return this.id;
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public Person(long id, @NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.id = id;
this.name = name;
this.age = age;
}
public final long component1() {
return this.id;
}
@NotNull
public final String component2() {
return this.name;
}
public final int component3() {
return this.age;
}
@NotNull
public final Person copy(long id, @NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
return new Person(id, name, age);
}
// $FF: synthetic method
public static Person copy$default(Person var0, long var1, String var3, int var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.id;
}
if ((var5 & 2) != 0) {
var3 = var0.name;
}
if ((var5 & 4) != 0) {
var4 = var0.age;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "Person(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
public int hashCode() {
long var10000 = this.id;
int var1 = (int)(var10000 ^ var10000 >>> 32) * 31;
String var10001 = this.name;
return (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31 + this.age;
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Person) {
Person var2 = (Person)var1;
if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
return true;
}
}
return false;
} else {
return true;
}
}
}
final类型,自动生成了get/set方法,hashCode方法,toString方法,equals方法,copy方法,还有就是几个component,有几个属性就有几个component,我们取属性的值就需要使用
val book = Book(0, "Kotlin", Person(1, "Jack", 20))
val id = book.component1()
val name = book.component2()
val author = book.component3()
如果想让这个类被继承,或者使用无参构造器,需要使用插件noArg和allOpen
枚举类 enum class
枚举定义
Java
enum State {
IDLE, BUSY
}
Kotlin
enum class State {
IDLE, BUSY
}
枚举属性
名字和序号
Java
State.IDLE.name();
State.IDLE.ordinal();
Kotlin
State.IDLE.name
State.IDLE.ordinal
定义构造器
Java
enum State {
IDLE(0), BUSY(1);
int id;
State(int id) {
this.id = id;
}
}
Kotlin
enum class State(var id: Int) {
IDLE(0), BUSY(1)
}
实现接口
这里可以统一实现,也可以单个实现
Java
enum State implements Runnable{
IDLE(0){
@Override
public void run() {
}
}
,
BUSY(1){
@Override
public void run() {
}
}
;
int id;
State(int id) {
this.id = id;
}
@Override
public void run() {
}
}
Koltin
enum class State(var id: Int) :Runnable{
IDLE(0) {
override fun run() {
}
}, BUSY(1) {
override fun run() {
}
};
override fun run() {
}
}
扩展方法
这个Kotlin中才行
fun State.next(): State {
return State.values().let {
val nextState = (ordinal + 1) % it.size
it[nextState]
}
}
区间
枚举类可以当做区间使用
val state = State.IDLE..State.BUSY
val currentState = State.BUSY
val flag = currentState in state
密封类 sealed class
实际上是一个特殊的抽象类,子类个数固定,构造器私有化
sealed class PlayerState
object Idle : PlayerState()
class Playing(val song: Song) : PlayerState() {
fun start() {}
fun stop() {}
}
class Error(val errorInfo: ErrorInfo) : PlayerState() {
fun recover() {}
}
内联类 inline class
- 对某一个类型进行包装
- 类似
Java包装类 - 编译器会尽可能使用被包装的类型进行优化
inline class BoxInt(val value: Int)
使用inline,主构造器必须是一个成员,不能私有化,必须使用val修饰
基本上里面只能定义方法
模拟枚举
inline class State(val ordinal: Int) {
companion object {
val Idle = State(0)
val Busy = State(1)
}
fun values() = arrayOf(Idle, Busy)
val name: String
get() = when (this) {
State.Idle -> "Idle"
State.Busy -> "Busy"
else -> throw IllegalArgumentException()
}
}