这一年开发中没有使用kotlin ,果然又不会写了,还是记下吧
本篇记录在Kotlin 中有关类的一些知识点,包括如下
1.1 声明类
- 声明一个类,如下:
class Person {
}
1.2 构造函数
1.2.1)主构造函数
- 主构造函数是类头部的一部分,它跟在类名的后边,使用关键字constructor 如下:
class Person constructor(name: String) {}
- 如果这个主构造函数没有任何注解或者可见的修饰符,这个constructor关键字可以省略掉,如下:
class Person (name: String) {}
- 如果主构造函数有注解或者可见的修饰符,那么关键字constructor是必须的 ,如下:
class Person public constructor(name: String) {}
1.2.2) init
主构造函数不能包含任何代码,初始化代码可以放置在初始化的模块,以init为前缀的关键字,如下:
class Person (name: String) {
init {
println("初始化数据");
}
}
- 2.2.1 可以把主构造函数中的参数变量赋值给类的属性 如下:
class Person (name: String) {
var name: String = "";
init {
println("初始化数据");
this.name = name;
}
}
- 也可以直接将主构造函数中的参数直接声明成类的属性,定义的方法就是在主构造函数的参数前面添加var 或val关键字,将上面的代码改为如下:
class Person (var name: String) {
init {
println("初始化数据");
name ="小红"
println("$name")//可以直接使用
}
}
- 可以像函数参数一样为构造方法参数声明一个默认值,如下:
class Person (var name: String = "小红") {
init {
println("初始化数据");
name ="小明"
println("$name")//可以直接使用
}
}
注意:
- 如果所有的构造方法参数都有默认值,那么编译器会生成一个额外的不带参数的构造方法来使用所有的默认值,这样可以通过无参构造方法来实例化
- 如果没有给一个类声明任何的构造方法,将会生成一个不带任何参数的默认的构造方法,当有类继承它时也必须显式的调用父类的构造方法,即使他没有任何参数
如:
class Student : Person(){}
- 如果你想要你的类不被其他代码实例化,必须把构造方法标记为private
例子:
class Boy private constructor(): Base(){
}
1.2.3)二级构造函数(一个或多个)
- 二级构造函数也是使用关键字 constructor ,与主构造函数不同的是位于类体中,如下:
open class MyView {
constructor(ctx: Context) {
//
}
constructor(ctx: Context, attr: AttributeSet) {
//
}
}
class MyButton : MyView {
constructor(ctx: Context) : super(ctx) {//调用父类的构造方法
//
}
constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr) {
//
}
}
注意:
-
如果类没有构造方法,那么每个从构造方法必须初始化基类或者委托给另一个这样做了的构造方法
-
如果类中存在主构造函数,那么每个二级构造函数需要委托给主构造函数
-
委托给构造函数使用关键字this,如下:
class Person public constructor(var name: String) {
//二级构造函数
constructor(name: String, parent: Person) : this(name) {
}
}
class Person constructor(var name: String) {
var age: Int = 0;
var sex: String = "";
init {
println("初始化数据");
this.name = name;
}
//二级构造函数 -- 委托给主构造函数
constructor(name: String, age: Int) : this(name) {
this.age = age;
}
//委托给两个参数的构造函数
constructor(name: String, age: Int, sex: String) : this(name, age) {
this.sex = sex;
}
}
1.3 创建类的实例
val person2: Person = Person("小红", 12);
println("name:" + person2.name + "age:" + person2.age);
2. 继承
2.1)继承类
在Kotlin中,默认情况下所有的类都是final 不可被继承,如果希望让别的类可以继承,使用关键字** open ,此外需要给每个可以被重写的属性或方法添加 open ** 修饰符
- 声明一个可被继承的类,如下:
open class Base {
}
- 继承Base类
class Boy : Base(){
}
- 如果要重写方法,在父类中允许重写的方法前面也要使用open 关键字
在子类中要重写方法使用override修饰方法,如下:
open class Base {
open fun getName(): String {
return "小明"
}
}
open class Boy : Base(){
fun getSex(){}//这个函数是final的,不能被重写
open fun getAge(){}//这个方法是open可以被子类重写
override fun getName(): String {//这个函数重写了一个open函数并且它本身同样是open的
return super.getName()
}
}
- 如果父类的这个函数没有标注open,则子类中不允许定义同名函数,不论加不加override。
- 在一个final类中(即没有声明open的类),函数上也不允许加open标注。
注意:
- 如果重写了一个基类或者接口的成员,重写了的成员同样默认是open的。如果你想改变这一行为,阻止你的子类再重写你的实现,可以显示的标记为final
- 当类未声明构造函数时,继承其他类时,也不需要在primary constructor中显示的声明构造函数,必须在secondary constructor中显示调用父类构造函数,若父类有多个构造函数,可选择其一进行调用:
如:
open class User(name: String) {
/**
* 二级构造函数 ,委托主构造函数
*/
constructor(name: String, age: Int) : this(name) {
}
}
/**
* 子类继承User类
*/
class Student : User {
/**
* 构造函数
*/
constructor(name: String) : super(name) {
}
/**
* 第二个构造函数
*/
constructor(name: String, age: Int) : super(name, age) {
}
}
2.2)重载规则
当类同时继承类和实现接口,且有相同方法,且相同方法都有实现时,需要在重载方法中调用所继承的方法,使用关键词 super ,T表示所继承或实现的接口,如下:
//父类
open class User{
open fun study(){
println("User -> study");
}
}
//接口
interface Reading {
//接口中的方法已被实现
fun study() {
println("Reading -> study");
}
}
class Student : User() ,Reading {
override fun study() {
//以下至少要继承一个
super<Reading>.study()
// super<User>.study()
}
}
- 当接口未实现方法时,默认为父类User的study方法,不需要调用所继承的方法
open class User{
open fun study(){
println("User -> study");
}
}
interface Reading {
fun study();
}
class Student : User() ,Reading {
override fun study() {
}
}
3. 抽象类
- 抽象类允许有abstract修饰的成员方法,非抽象类不允许有抽象方法
- 抽象成员始终是open,不需要显示的使用open
abstract class User{
abstract fun study()
}
class Person{
abstract fun study() // 编译错误
}
- 抽象类默认是可被继承的,接口是特殊的抽象类,允许有抽象方法,如下:
interface Reading{
abstract fun reading()
}
abstract class Animated {
abstract fun animated()//此函数为抽象函数,必须被子类重写
//抽象类中的非抽象函数并不是默认open的,但是可以标注为open
open fun stopAnimating() {}
fun animateTwice() {}
}
4. 接口
kotlin 中接口的方法可以有默认实现,不需要特殊的注解,只需要提供一个方法体
例子:
interface Clickable {
fun click()
fun showOff() = println("showOff")//含有默认实现的接口
fun hideOff() = println("hideOff")
}
java实现包含默认实现的接口 Kotlin 1.0是以java
6为目标设计的,其并不支持接口中的默认方法,因此它会把每个带默认方法的接口编译成一个普通接口和一个将方法体作为静态函数的类的结合体,接口中只包含声明,类中包含了以静态方法存在的所有实现,因此如果要在java中实现这样的接口,必须为所有的方法,包括在kotlin中带有方法体的方法定义你自己的实现
5. 伴生对象 companion
- 不像 Java 或者 C#,在 Kotlin 中Class 没有静态方法。在大多数情况下,推荐用 package-level 的函数来代替静态方法。
- 如果你需要写一个不需要实例化 Class 就能访问 Class 内部的函数(例如一个工厂函数),你可以 把它声明成 Class 内的一个实名 Object。
- 另外,如果你在 Class 内声明了一个 companion object,在该对象内的所有成员都将相当于使用了 Java/C# 语法中的 static 修饰符,在外部只能通过类名来对这些属性或者函数进行访问。
companion 的使用,如下:
class Utils {
companion object Utilss {
fun isEmpty(string: String?): Boolean {
return string != null && string.length == 0
}
fun isWeakEmpty(string: String): Boolean {
return isEmpty(string) && string.trim { it <= ' ' }.length == 0
}
}
}
- 调用方式如下:
Utils.isEmpty(username)
6 .内部类
- Kotlin同样支持嵌套的内部类,不过和java不一样,Kotlin的内部类不会默认包含一个指向外部类对象的引用,也就是说Kotlin中所有的内部类默认就是静态的,这样可以减少很多内存泄露的问题
- 如果需要在内部类中引用外部类对象,可以在内部类声明前加Inner关键字,然后在Inner类中使用this@外部类来指向外部类对象
如:
class Student : User() ,Reading {
override fun study() {
}
inner class Boy(){
fun get(){
//内部类引用外部类对象
this@Student.study();
}
}
}
7. 数据类
我们常常会创建一个只包含数据的类,其他什么事情都不做。 在Kotlin中,这样的类叫做数据类,表示关键字为data
7.1)声明数据类,如下:
data class User(var name: String, var age: Int)
7.2)使用了data 编译器会自动的生成一下函数
- equals() / hashCode()
- toString() 格式如 : “User(name=John, age=42)”
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数 (见下文).
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
7.3)数据类需要满足以下条件:
- 主构造函数至少包含一个参数。
- 所有的主构造函数的参数必须标识为val 或者 var ;
- 数据类不可以声明为 abstract, open, sealed 或者 inner;
- 数据类不能继承其他类 (但是可以实现接口).
8. 枚举类
每一个枚举都是枚举类的实例
8.1)定义枚举类,如下:
enum class Color {
RED,ORANGE,YELLOW,GREEN ,BLUE,INDIGO,VIOLET
}
enum class Color(val r: Int,
val g: Int,
val b: Int) {
RED(255, 0, 0),
ORANGE(255, 165, 0),
YELLOW(255, 255, 0),
GREEN(0, 255, 0),
BLUE(0, 0, 255),
INDIGO(75, 0, 130),
VIOLET(238, 130, 238)
}
8.2)给枚举类添加方法,如下:
注意:要使用分号把枚举常亮列表和方法定义分开
enum class Color(val r: Int,
val g: Int,
val b: Int) {
RED(255, 0, 0),
ORANGE(255, 165, 0),
YELLOW(255, 255, 0),
GREEN(0, 255, 0),
BLUE(0, 0, 255),
INDIGO(75, 0, 130),
VIOLET(238, 130, 238);
//给枚举类添加方法,注意要使用分号把枚举常亮列表和方法定义分开
fun rgb() = (r * 256 + g) * 256 + b
}
9.类属性的访问器(getter setter)
9.1)声明一个只读属性,一个可读写属性的类,如下:
class User(var id: Int, //只读属性,生成一个字段和一个getter方法
val name: String // 可写属性:生成一个字段和 getter setter 方法
){}
- 在类中声明一个属性和声明一个变量一样,使用val和 var关键字,声明成val的属性是只读的,而var属性是可变的
- 当你声明了属性,就声明了对应的访问器(只读属性有一个getter,而可写属性既有getter也有setter)
调用
//可以直接访问属性
val user: User = User(1,"name")
val id = user.id//自动调用了getter方法
user.id = 1//自动调用了setter
user.name//调用了getter方法
9.2) 自定义访问器
class User {
var id: Int
get() = id //声明属性的getter
set(value) { //声明属性的setter
id = value
}
val name: String
get() = name
}
例子:
var AlertDialog.gravity: Int
get() {
val window = window
val lp = window.attributes
return lp.y
}
set(value) {
val window = window
val lp = window.attributes
lp.gravity = value
}
9.3) 修改访问器的可见性
class User {
var id: Int = 0
private set //设置属性id的set方法不可见
val name: String = ""
}