一、面向对象编程简史
参考《kotlin从入门到进阶实践》55页。
二、声明类
1、空类
使用class关键字声明类。可以声明一个什么都不干的类(空类)
2、声明类和构造函数
1)声明类的时候同时声明构造函数,语法格式 是在类的后面用括号包含构造函数的参数列表。(
使用最多)
class Person(var name : String,var age : Int) {
override fun toString(): String {
return "Person {name = $name, age = $age}"
}
}
// 调用
val person = Person("太难了",35)
println("person = ${person}")
执行结果:
2)也可以先声明属性,等构造实例对象的时候再去初始化属性值。
class Person {
lateinit var name : String // lateinit 关键字表示该属性延迟初始化
var age : Int = 0 // lateinit 关键字不能修饰primitive类型(Int ,Float)
lateinit var sex : String //
override fun toString(): String {
return "Person {name = $name, age = $age, sex = $sex}"
}
}
// 调用
val person = Person()
person.age = 27
person.name = "后来"
person.sex = "W"
println("person = ${person}")
执行结果:
3) 声明一个具有多种构造方式的类,可以使用constructor关键字声明构造函数
class Person {
lateinit var name : String // lateinit 关键字表示该属性延迟初始化
var age : Int = 0 // lateinit 关键字不能修饰primitive类型(Int ,Float)
lateinit var sex : String
constructor(name : String) { // 次级构造函数
this.name = name
}
constructor(name: String, age : Int) { // 次级构造函数
this.name = name
this.age = age
}
constructor(name: String,age: Int,sex : String) { // 次级构造函数
this.name = name
this.age = age
this.sex = sex
}
override fun toString(): String {
return "Person {name = $name, age = $age, sex = $sex}"
}
}
// 调用
val person = Person("原来你也在这里", 32,"W")
println("person = ${person}")
执行结果:
4)使用AS自动生成二级构造函数
步骤一:在类中声明变量
步骤二:在当前类中右击,在弹出的快捷菜单中选择Generate命令
步骤三:弹出生成次级构造函数的对话,在其中选择Secondary Constructor命令
生成之后的结果:
当需要通过比较复杂的逻辑来构建一个对象的时候,可采用构建者(Builder)模式来实现
三、抽象类与接口
抽象类表示“is-a”关系,而接口所代表的是“has-a”。
1、抽象类与抽象成员
1)抽象类用abstract。
2)因为抽象的概念在问题领域中没有对应的具体概念,所以抽象类是不能够实例化的。
3)只能实例化它的继承子类。
4)抽象类的成员(属性和函数)也必须是抽象的,需要使用abstract修饰。
// 定义抽象类 及 抽象方法
abstract class Shape {
abstract fun area() : Double // 抽象方法
abstract fun perimeter() : Double // 抽象方法
}
// 继承抽象类
class Rectangle(val width : Double, val height : Double) : Shape() {
override fun area(): Double {
return height * height
}
override fun perimeter(): Double {
return 2*(width + height)
}
}
// 调用
val rec = Rectangle(4.0,8.0);
println("shape : are = " + rec.area() + ",perimeter = " + rec.perimeter())
// 执行结果
I/System.out: shape : are = 32.0,perimeter = 24.0
5) 抽象类可以有带实现的函数。在所有子类都可以直接调用这个函数
abstract class Shape {
abstract fun area() : Double // 抽象方法
abstract fun perimeter() : Double // 抽象方法
// 带实现的函数
fun onClick() {
println("shape: I am clicked!")
}
}
// 调用
val rec = Rectangle(4.0,8.0);
println("shape : are = " + rec.area() + ",perimeter = " + rec.perimeter())
rec.onClick()
// 输出
I/System.out: shape: I am clicked!
6) 父类中的onClick()函数默认是final的,不可被覆盖重写。如果想要开放给子类重新实现这个函数,可以在前面加上open关键字:
abstract class Shape {
abstract fun area() : Double // 抽象方法
abstract fun perimeter() : Double // 抽象方法
// 带实现的函数
open fun onClick() {
println("shape: I am clicked!")
}
}
// 继承类重写
class Rectangle(val width : Double, val height : Double) : Shape() {
override fun area(): Double {
return (width * height)
}
override fun perimeter(): Double {
return 2*(width + height)
}
override fun onClick() {
// super.onClick()
println("shape : ${this::class.simpleName} I am clicked!")
}
}
// 调用
val rec = Rectangle(4.0,8.0);
println("shape : are = " + rec.area() + ",perimeter = " + rec.perimeter())
rec.onClick()
// 输出
I/System.out: shape : Rectangle I am clicked!
当子类继承了某个类之后,便可以使用父类中的成员变量,但并不是完全继承父类所有成员变量。具体原则如下:
1)能够继承父类的public和protected成员变量;
2)不能继承父类的private成员变量;
3)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能继承;
4)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,既子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中的同名成员变量,需要使用super关键字进行引用。
2、接口
1) 使用interface作为关键字;
2) 与抽象方法相比,接口都可以包含抽象的方法既方法实现;
3) 接口是没有构造函数的。使用冒号“:”语法来实现一个接口,如果有多个接口,用“,”逗号隔开。
4) 在重写某个方法时,如果实现的多个接口都实现了该方法,使用 super<接口名>.方法 调用。
四、object对象
单例模式可以保证系统中只有一个实例。既一个类只有一个对象实例。kotlin中没有静态属性和方法,但是可以使用关键字object声明一个object单例对象。object修饰的类是单例。
kotlin中还提供了伴生对象,用companion object(相当于java中的static)关键字声明。一个类只能有一个伴生对象。companion objec 属于成员的单例。
open class Foo() {
object A{
val a = 2;
}
companion object {
val b = 3;
}
}
println("the Foo a = " + Foo.A.a)
println("the Foo b = " + Foo.b)
// kotlin 转换为java
public class Foo {
private static final int b = 3;
public static final class A {
private static final int a;
@NotNull
public static final Foo.A INSTANCE;
public final int getA() {
return a;
}
private A() {
}
static {
Foo.A var0 = new Foo.A();
INSTANCE = var0;
a = 2;
}
}
}
五、数据类
数据类是只存储数据,不包含操作行为的类。Kotlin 中的数据类型可以不需要写get set 样本代码。
1、创建数据类
使用关键字data class创建一个只包含数据的类:
data class LoginUser(val name : String,val password : String)
在AS 上 可以使用工具,自动生成对应的java代码,通过java代码可以看出,数据类中自动实现的get set方法。
2、数据类自动创建的函数
编译器会根据主构造函数中声明的属性,自动创建以下3个函数:
1)equals()/hashCode()函数。
2)toString() 格式为:
@NotNull
public String toString() {
return "LoginUser(name=" + this.name + ", password=" + this.password + ")";
}
3)component1()和component2()函数返回对应下表的属性值,按声明顺序排列;
4)copy()函数:根据旧对象属性重新new LoginUser(name, password) 一个对象出来。
3、数据类的语法限制
1)主构造函数至少包含一个参数;
2)参数必须标识为val 或者var;
3)不能为abstract open sealed 或者 inner;
4) 不能继承其他类(但能实现接口)
数据类可以在解构声明中使用:
// 数据类
data class LoginUser(val name : String,val password : String)
// 调用
val user = LoginUser("一辈子孤单","123456")
val (myName, myPassword) = user // 解构声明(myName, myPassword)
println("data class : name = $myName, password = $myPassword")
// 输出
I/System.out: data class : name = 一辈子孤单, password = 123456
4、Pair 和Triple
Kotlin标准库提供了Pair和Triple数据类。分别表示二元组和三元组对象。
使用Pair对象初始化一个Map,代码如下:
val map = mapOf( 1 to "A", 2 to "B",3 to "C")
println("data class : map = $map")
// 输出
I/System.out: data class : map = {0=A, 2=B, 3=C}
六、注解
没看懂。参考《Kotlin从入门到进阶实战》70页
七、枚举
使用enum class关键字声明一个枚举类。
enum class Direction {
NORTH,SOUTH,WEST,EAST
}
相比字符串常量,使用枚举能够实现类型安全。枚举类有两个内置的属性name和ordinal 分别表示的是枚举的对象值和下标位置。
// 枚举
val west = Direction.WEST
println("enum : name = " + west.name + ",ordinal = " + west.ordinal)
// 输出
I/System.out: enum : name = WEST,ordinal = 2
每一个枚举都是枚举类的实例,他们可以被初始化:
enum class Direction(val c : String) {
NORTH("北"),
SOUTH("南"),
WEST("西"),
EAST("东")
}
// 调用
val west = Direction.WEST
println("enum : name = " + west.name + ",ordinal = " + west.ordinal + ",west = $west" + ",c = " + west.c)
// 输出
I/System.out: enum : name = WEST,ordinal = 2,west = WEST,c = 西
八、内部类
1、普通嵌套类
class NestedClassesDemo {
class Outer {
private val zero : Int = 0
val one : Int = 1
class Nested {
fun getTwo() = 2
class Nested1 {
val three : Int = 3
fun getFour() = 4
fun acccessOut() {
// println("Nested : one = $one")
}
}
}
}
}
普通嵌套类没有持有外部类的引用,所以无法访问外部类的变量。
2、嵌套内部类
如果一个内部类想要访问外部类的一个成员,可以在这个类前面添加修饰符inner.
class NestedClassesDemo {
class Outer {
private val zero : Int = 0
val one : Int = 1
inner class Nested {
fun getTwo() = 2
inner class Nested1 {
val three : Int = 3
fun getFour() = 4
fun acccessOut() {
println("Nested : one = $one")
println("Nested : two = ${getTwo()}")
}
}
}
}
}
// 调用
NestedClassesDemo.Outer().Nested().Nested1().acccessOut()
// 输出
I/System.out: Nested : one = 1
I/System.out: Nested : two = 2
3、匿名内部类
class NestedClassesDemo {
class AnonymousInnerClassDemo {
var isRunning = false
fun doRun() {
Thread(object : Runnable { // 匿名内部类
override fun run() {
isRunning = true
println("doRun : i am running ,isRunning = ${isRunning}")
}
}).start()
}
}
}
匿名内部类就是没有名字的内部类。匿名内部类可以访问外部类的变量。