一、抽象类与接口(abstract,interface)
- 1.抽象类
- 1.可以简单的理解为:实现一部分协议的半成品(类可以看成成品),比类抽象点,比接口具体点;
- 2.抽象类是有类的特征的;
- 3.抽象类可以有属性,可以有方法,并且方法可以有方法体;
- 4.要想重写抽象类中的具体方法,需要将此方法用open修饰符修饰,才能重写;
- 5.必须由子类继承后使用;
- 6.抽象类默认就是open的,里面的抽象方法默认也是open的;具体方法如果想要被子类重写必须要用open修饰;
abstract class C {
//抽象类可以有状态--成员变量和具体方法
var i = 0 //这个值可以变
open fun test() {
}
//抽象方法不能有函数体,必须由子类实现
abstract fun abstractMethod()
}
/**
* 如果继承的是抽象类或者类,必须写上父类的构造方法
*/
class D: C() {
override fun abstractMethod() {
}
//要想复写抽象类的具体方法,必须加上修饰符open,否则访问不到
override fun test() {
super.test()
}
}
- 2.接口
- 1.可以简单的理解为:协议,规定;
- 2.在kotlin可以给接口方法一个默认实现,但是默认实现不能有状态,状态必须在实现类中定义;
- 3.Kotlin的接口与Object-C的Protocol比较类似;
- 4.接口不能有状态;
- 5.必须由类对其进行实现后使用;
- 反应事物的能力;
interface TestInterface {
//可以定义,但是不能初始化
//这里相当于定义了一个setI()和getI()的接口方法,不是真正定义了一个状态变量,
//因为不能写get方法
val i :Int
//接口只能有无状态的默认实现
fun interfaceMethod(){
println(i)
}
}
class ImplTest : TestInterface {
//必须实现成员变量,否则编译报错
override val i: Int
get() = 0
//由于TestInterface中的方法有个默认实现,在这里可以不用实现此方法
// override fun interfaceMethod() {
// super.interfaceMethod()
// }
}
//成员变量的实现还可以用构造器的方式实现
interface TestInterface {
val i :Int
fun interfaceMethod(){
println(i)
}
}
class ImplTest(override val i: Int) : TestInterface {
//必须实现成员变量,否则编译报错
// override val i: Int
// get() = 0
//由于TestInterface中的方法有个默认实现,在这里可以不用实现此方法
// override fun interfaceMethod() {
// super.interfaceMethod()
// }
}
总结:
1.抽象类具有类的特征,可以有内部的状态(也就是可以有成员变量和具体方法);接口可以有不能初始化的成员变量,这个成员变量其实就是一个接口方法;也就是说抽象类有状态,接口没有状态;抽象类有方法实现,接口只能有无状态的默认实现;
2.单继承多实现;
3.抽象类反映事物的本质,不管男人、女人,本质都是人;
4.接口反映事物的能力,能力可以有多个,但是你还是你--说的是抽象类;
5.如果要用抽象类和接口描述一个东西,那这个东西的中心词前面的修饰词就是接口要负责的部分,中心词是抽象类负责的部分;
举个例子:Mac 笔记本 电脑
那么中心词是“电脑”--电脑用抽象类定义;
Mac和笔记本是修饰词--用接口定义;
接口与抽象类的共性:
1.比较抽象,不能直接实例化;
2.需要由子类或者实现类实现他们的抽象方法;
3.父类(接口)变量可以接受子类(实现类)的实例赋值;也就是父类的引用指向子类;
备注:如果不是抽象类,想要被继承的话,必须加上open关键字,否则无法被继承,默认为final;类中的方法想要被覆写,也要先open;abstrac类、abstract方法和接口、接口方法不需要写open,默认就是open
/**
* 如果想要被继承,必须用open修饰
* 属性想要被覆写,也必须open修饰
*/
open class Person(open val age: Int,open var name:String){
//如果方法想要被子类重写,必须用open修饰
open fun work(){
}
}
//覆写属性时,必须用voerride
class MaNong(override val age: Int,name:String): Person(age,name) {
//子类重写父类的方法,必须要用override修饰
override fun work() {
super.work()
}
}
class Enginer(age: Int,name:String) : Person(age,name){
//覆写属性的第二种方式
//val修饰的属性只有get方法,因为不可变,不能有set方法
override val age: Int
get() = 0//重新赋值
override var name: String = ""
get() = super.name
set(value) {
field = value
}
}
- 通过by关键字实现接口代理
interface Driver{
fun drive()
}
interface Writer{
fun write()
}
class ImpDriver :Driver {
override fun drive() {
println("驾驶")
}
}
class ImpWriter:Writer {
override fun write() {
println("写作")
}
}
/**
* 通过by关键字实现接口代理
*/
class ImplPreson(val driver: Driver,val writer: Writer):Driver by driver,Writer by writer{
//通过使用by关键字进行接口代理,下面的方法不用实现
// override fun drive() {
// driver.drive()
// }
//
// override fun write() {
// writer.write()
// }
}
fun main(args: Array<String>) {
var driver = ImpDriver()
var writer = ImpWriter()
var person = ImplPreson(driver,writer)
person.drive()
person.write()
}
二、类的鼻祖-object-单例
1.在实际的使用中只有一个对象--也就是kotlin中的单例;相当于java中的最简单的单例实现;
2.使用时可以和普通的类一样,可以有成员和方法,只是class的一种特殊情形;
3.可以实现接口,继承类;
interface Play{
fun play()
}
abstract class Sleep {
abstract fun sleep()
}
object Manager :Play,Sleep(){
override fun play() {
}
override fun sleep() {
}
lateinit var name:String
fun work() {
}
}
fun main(args: Array<String>) {
Manager.work()
Manager.sleep()
Manager.name = "android"
}
三、伴生对象与静态成员--companion object
1.用来实现静态方法和静态属性;
2.如果想和Java中静态方法和属性的调用一直的话,可以在静态属性上添加@ JvmField,在静态方法上添加@ JvmStatic
实现方式如下:
fun main(args: Array<String>) {
var latitude = Latitude.ofDouble(3.0)
var copy = Latitude.copy(latitude)
println( Latitude.param )
}
class Latitude private constructor(val value: Double)
{
companion object {
@JvmField
//静态属性
val param:Double = 100.0
@JvmStatic
//静态方法
fun ofDouble(double: Double):Latitude {
return Latitude(double)
}
fun copy(latitude: Latitude):Latitude{
return Latitude(latitude.value)
}
}
}
总结:
1.每个类都对应一个伴生对象,相当于类对应的静态成员;
2.伴生对象的成员全局独一份,这个是针对于类来说的;
3.伴生对象的成员相当于我们Java中的静态成员;
3.静态成员尽量使用包级函数、包级变量代替;
4.在Java中调用kotlin中的伴生对象时,kotlin中的伴生对象使用JvmField和JvmStatic注释可以保证使用时调用一致性;
四、方法重载和默认参数--overload
1.方法重载--方法名相同,参数不同;与返回值无关;
JVM函数签名:只与函数名和参数列表相关;与返回值无关;
class Overloads {
fun a(): Int {
return 0
}
fun a(param: Int ): Int {
return param
}
}
fun main(args: Array<String>) {
var overload = Overloads()
overload.a()
overload.a(10)
}
2.方法重载可以转换为默认参数;
可以为任意位置的参数设置默认参数;函数调用产生混淆时用具名函数
class Overloads {
// fun a(): Int {
//
// return 0
// }
//这个注解是为了给Java中调用kotlin中的重载方法使用
@JvmOverloads
fun a(param: Int = 0): Int {
return param
}
}
fun main(args: Array<String>) {
var overload = Overloads()
overload.a()//不传任何参数,会使用默认参数0
overload.a(10)
}
五、扩展成员
1.为现有类添加方法、属性;
fun X.y():Z{...}
val X.m 注意扩展属性不能初始化,类似接口属性
2.Java调用扩展成员类似调用静态方法;
下面展示扩展属性和扩展方法:具体的看下面的示例:
fun main(args: Array<String>) {
println("abc".copy(4))
//输出值:abcabcabcabc
println("abc".param)
//输出值:扩展属性
}
/**
* 扩展方法使用XXX.方法名
* 那么在方法中this就代表XXX
*/
fun String.copy(int: Int) :String{
var stringBuilder = StringBuilder()
for (i in 0 until int) {
stringBuilder.append(this)
}
return stringBuilder.toString()
}
/**
* 扩展属性只用val修饰,只能有get方法;用var修饰虽然有set方法,但是无法使用field
* 它的意思是所有的字符串都有个param属性,其实就是调用了get方法
*/
val String.param : String
get() = "扩展属性"
var Int.params:Int
set(value) {}
get() = 100
六、属性代理
定义方法:
val/var <property name>:<Type> by <expression>
Type类型可以不写,让编译器自行推导;
expression代表的代理对象;
代理者需要实现相应的setValue/getValue方法;
代理的val只实现setValue方法,代理var的需要实现setValue和getValue方法;
import kotlin.reflect.KProperty
/**
* Created by serenitynanian on 2017/7/14.
* 属性代理
*/
class Delegates{
//一旦被lazy by了,就相当于只有在第一次访问到hello时才会初始化
//lazy就是代理,对于val的变量的代理,必须实现getValue方法
val hello by lazy {
"Hello world"
}
//对hello1进行获取的时候,相当于hello1的值交给了X的getValue
val hello1 by X()
//对hello2设置值的时候,相当于调用了X的setValue;
//对hello2进行获取的时候,相当于调用了X的getValue方法
var hello2 by X()
}
class X{
private var value:String? = null
//下面的这个方法拷贝的lazy中的getValue方法,进行了一点改造
/**
* thisRef代表的是Delegates对象 property.name代表的是代理的变量名
*/
operator fun getValue(thisRef: Any?, property: KProperty<*>): String{
println("getValue------>$thisRef-------->$property")
return value?:" no value"
}
//setValue方法最少需要三个参数
operator fun setValue(thisRef: Any?, property: KProperty<*>,value:String){
println("getValue------>$thisRef-------->$property------>$value")
this.value = value
}
}
fun main(args: Array<String>) {
val delegates = Delegates()
println(delegates.hello)
println(delegates.hello1)
println(delegates.hello2)
delegates.hello2 = "set value ing..."
println(delegates.hello2)
/**
*打印值:
* Hello world
getValue------>kotlindemo.Delegates@548c4f57-------->property hello1 (Kotlin reflection is not available)
no value
getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)
no value
getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)------>set value ing...
getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)
set value ing...
*/
}
七、数据类(allOpen,noArg插件)
这个数据类类似Javabean,里面默认实现了get和set、toString方法,但是不同点是这个data class是被final修饰的,不能被继承,并且未提供无参的构造方法,所以不能完全替代javabean;为了能够实现javabean,官方提供了两个插件--allopen和noArg;
具体配置如下:
在项目下的build.gradle文件中配置如下:
配置后需要自定义注解类:
annotation class Poko
import annotations.Poko
/**
* Created by serenitynanian on 2017/7/17.
* 数据类(allOpen,noArg)类似java中的javabean
* 但是不同点是:看字节码这个被data修饰的类是final的,不能被继承,并且未提供无参构造方法
* Javabean可以被继承并且有无参构造方法;
*
*/
@Poko
data class Country(var id:Int,var name:String)
/**
* 这个类说明了component并不是data class的专利,只是data class自动给它生成了component
* 如果我们类中需要,我们就自己定义即可
*/
class Componnent1{
operator fun component1():String{
return "component1"
}
operator fun component2():Int{
return 100
}
operator fun component3():Int{
return 22
}
}
fun main(args: Array<String>) {
var country = Country(0,"chain")
println(country)
println(country.component1())
println(country.component2())
var(country_id,country_name) = country
println(country_id)
println(country_name)
// for ((index, value) in args.withIndex()) {
// println("Index----->"+index)
// println("Value------>"+value)
// }
var component = Componnent1()
var (a,b,c) = component
println("$a$b$c")
}
noArg给我们生成了无参的构造方法;
allOpen给我们生成了不是final类型的类;
不过这都是在编译器生成的,所以在代码中使用无参的是不能使用的,只能通过运行时反射得到,比如使用realm就可以使用这两个插件
八、内部类(this@Outter,this@Inner)
1.静态内部类不持有外部类的引用;
2.非静态内部类持有外部类的引用或状态;
3.kotlin中默认的内部类是静态的内部类;如果想使其为非静态的用inner关键字修饰
4.非静态内部类访问外部类的属性,用this@Outter.属性名;类似java中的外部类名.this.属性名;
5.如果内部类的实例需要依赖外部类的实例,就使用非静态内部类;如果内部类只是逻辑上与外部类有关联,那么实例的存在不依赖于外部类,就可以使用静态内部类;
6.kotlin中的匿名内部类,可以继承一个类,实现多个接口:object:类名(),接口{方法体};编译时生成类名,类似Outter$1.class
open class Outter{
var a :Int = 0
open fun OutterMethod() {
}
//默认静态的
class StaticInner{
fun hello() {
}
}
//inner修饰后为非静态的
inner class NoStaticInner{
var a:Int = 5
fun hello(){
//为了保证引用到外部类的属性,用this.Outter.属性名
println(this@Outter.a)
println(this@NoStaticInner.a)//引用内部的a属性
println(this.a)//引用内部的a属性
}
}
}
interface OnClickListener{
fun onClick()
}
class View{
var onClickListener:OnClickListener? = null
}
fun main(args: Array<String>) {
//从这可以看出Inner默认的就是静态内部类
//如果让Inner为非静态的,用inner修饰即可
var staticInner = Outter.StaticInner()
//用外部类的实例实例化
var inner = Outter().NoStaticInner()
var view = View()
//kotlin中的匿名内部类可以继承一个类,实现多个接口
view.onClickListener = object :Outter(),OnClickListener{
override fun onClick() {
}
override fun OutterMethod() {
super.OutterMethod()
}
}
}
九、枚举
实例的个数确定,枚举也是类;
可以修改构造,添加成员;
可以提升代码的整洁,但是会损耗点性能;(比如说一些可以直接用int代表的,如果用枚举,会多消耗内存);
/**
* Created by serenitynanian on 2017/7/17.
*/
enum class Season(var desc:String) {
SPRING("春天"), SUMMER("夏天"), FALL("秋天"), WINTER("冬天");//如果下面有方法,这里必须写分好
fun getTag() {
//name是枚举自己的默认属性,比如第一个name为:SPRING
println("$name------$desc")
}
}
/**
* 这个类就类似枚举类
*/
class Test private constructor()
{
companion object {
val SPRING = Test()
val SUMMER = Test()
val FALL = Test()
val WINTER = Test()
}
}
fun main(args: Array<String>) {
println(Season.SPRING.getTag())
println(Season.SPRING.ordinal)//此对象在整个枚举类中的次序
println(Season.valueOf("SPRING"))//获得Spring对象
Season.values().map(::print)//打印出所有对象
}
十、密封类(sealed classs)-子类有限的类,枚举是实例可数
1.其子类只能定义在与sealed class同一文件中或者在seal class内部(kotlin版本小于1.1);