這一年開發中沒有使用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 = ""
}