如果对象是函数式接口(只有一个抽象方法的接口),则可以使用带接口类前缀的Lambda表达式创建对象,如:Runnable
Var r=Runnable{
......(run()方法实现)
}
1、对象表达式
对象表达式成员:初始化块、属性、方法、内部类(不能包含嵌套类),不能有构造器
对象表达式相当于Java中的匿名内部类,但功能更强大:
- 对象表达式可以只用指定父类型,也可以有一个或多个父类型
语法格式:
object[:0-N个父类型(父类类、接口)]{
初始化块
属性
方法
内部类,不能包含嵌套类
}
- 对象表达式不能是抽象类,系统在创建对象表达式时就会立即创建对象,因此不允许将对象表达式定义为抽象类
- 对象表达式不能定义构造器,但可以有初始化块
- 对象表达式可以包含内部类,不能包含嵌套类
Kotlin中表达式:
-
表达式在局部范围内(函数或者方法中)或者使用private修饰对象表达式,Kotlin编译器可以识别对象表达式的真实类型;比如:
fun main(args:Array){
var obj=object {
var name=“leslie”
fun test(){} }
var obj2=object:outputable {
var name=“leslie”
fun test(){} override fun output(msg:String){ } }
}
这里编译器是可以识别obj变量的类型的,因此也就可以调用其变量以及方法,无论是父类重写的方法还是自己新增的方法、属性
-
***(作为成员变量)***非private修饰的对象表达式就和Java中的匿名内部类一样了,编译器只会把该对象表达式当成他父类(接口)处理,不能识别器真实类型,如果对象表达式没有父类,就当成Any类处理,自然对象表达式中新增的属性、方法就调用不到,只能调用继承、重写的方法、属性
class Sub{
//1、private修饰的对象表达式
private var obj={
var name:String=“leslie”override fun test(mas:String){ } } //2、非private internal var obj2=object:Outputable{ var name:String="leslie" override fun test(mas:String){ } } //3、(单表达式函数),对象表达式作为函数返回值 private fun say()=object:Outputable{ var name:String="leslie" override fun test(mas:String){ } }
//4、(单表达式函数),对象表达式作为函数返回值
internal fun say()=object:Outputable{
var name:String=“leslie”override fun test(mas:String){ } }
}
第一和第三种情况由于能识别变量真实类型调用其name属性(obj.name、say().name)自然没有问题,而第二种和第四种情况不能识别其真实类型,只能当做父类型处理,编译期类型即为父类型,由于没有name属性,自然报错;
还有一点就是:java中要求匿名内部类访问局部变量时,局部变量必须final修饰,就是说该局部变量不能别被修改,而Kotlin中可以修改该局部变量;
2、对象声明
其作用就是实现单例模式;
语法
object 对象声明名称[:0-N个父类型]{
//定义属性
//方法
//嵌套类,不能定义内部类(非静态内部类)
//初始化块
}
对象声明和对象表达式语法大体格式都一样,也有一些区别:
- 对象表达式是一个表达式(可以看做一个对象),可以被赋值给变量,而对象声明不是表达式,不能用于赋值
- 对象声明可以包含嵌套类,但不能包含内部类;而对象表达式只能包含内部类而不能包含嵌套类
3.*** 对象声明不能定义在函数或方法内,但对象表达式可嵌套在其他对象声明中或非内部类中***
对象声明定义位置
-
在kotlin文件中定义对象声明:
interface Outputable{
fun output(msg:String)
}abstract class Product(var price:Double){
abstract val name:String
abstract fun printInfo()
}object MyObj1:Outputable{
override fun output(msg: String) {
Log.d(“TAG”,“重写从父类继承到的方法”)
}}
object MyObj2{
init {
Log.d(“TAG”,“初始化块”)
}
var name=“Kotlin”
fun test(){
Log.d(“TAG”,“test方法”)
}
class Foo}
调用:MyObj1.output(“输出设备”)
-
在类体中定义
class ObjectDecl{ object MyObj3{ init { Log.d("TAG","定义在类体中的对象声明的初始块") } var name:String="leslie" class Foo fun info(){ Log.d("TAG","定义在类体中的对象声明的方法") } } }
调用:ObjectDecl.MyObj3.info()
对象声明所定义的对象是该类的唯一实例,通过对象声明的名称直接访问该类的唯一实例;
MyObj1.output(“输出设备”)
在类中定义对象声明
伴生对象
在类中定义对象声明,使用"companion"修饰符,这样对象就变成了伴生对象;
伴生对象作用:就是为外部类提供静态属性、静态方法,弥补kotlin没有静态成员的不足(嵌套类可以看做外部类的静态成员),伴生对象中的方法、属性就可看做外部类的静态属性、静态方法
- 一个类最多只能定义一个伴生对象,伴生对象就相当于外部类的对象,可以直接通过外部类直接调用伴生对象的成员;
- 伴生对象的名称可以省略,如果省略后,要访问伴生对象,就通过Companion名称访问伴生对象即:外部类.Companion;那伴生对象可以访问了,访问伴生对象的方法、属性也就轻而易举了,即:外部类.Companion.属性/方法
语法:
companion object 伴生对象名称:(0-N)父类型{
//定义属性
//方法
//嵌套类,不能定义内部类(非静态内部类)
//初始化块
}
如:
interface Outputable{
fun output(msg:String)
}
class MyClass{
companion object MyObj3:Outputable {
//伴生对象中属性相当于外部类静态成员变量
val name="name属性值"
//伴生对象中的方法相当于外部类中静态方法
override fun output(msg: String) {
for (i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
}
fun main(args:Array<String>){
MyClass.output("leslie")
println(MyClass.name)
}
Kotlin中取消了static修饰符,伴生对象就是Kotlin弥补中没有静态成员的不足;从上面的例子可以看出伴生对象中的属性、方法分别相当于外部类的静态变量、静态方法.
注意:这里Kotlin只是利用伴生模拟java中静态成员,但伴生对象的属性、方法依然是伴生对象的实例成员,并不属于伴生对象所在的外部类;
伴生对象扩展方法
如果一个类具有伴生对象,允许为伴生对象扩展方法和属性,而为伴生对象扩展方法和属性,就就相当于为外部类扩展静态变量、静态方法
如:
fun MyClass.Object3.test(){}
val MyClass.Object3.foo
get()="lelsie"
如果伴生对象省略了名称
fun Myclass.Companion.test(){
println("伴生对象扩张方法")
}
val MyClass.Companion.info
get()="leslie"
总结:关于伴生对象、对象表达式、对象声明
对象表达式:(1)、实现的java中匿名内部类功能 (2)、可以定义在文件、函数或方法中、或者类中
对象声明 :(1)、实现的是java中单例类功能、(2)、可以定义在文件、类中、不能定义在函数或者方法中
伴生对象 :(1)、实现为类提供静态成员的功能 (2)、只能定义在类中
当三者定义在类中:
- 对象表达式对于外部类来说看做实例变量,可以访问外部类所有成员(静态、实例成员变量),但是外部类不能访问对象表达中成员变量
- 对象声明对于外部类:相当于一个静态成员变量(静态内部单例类),不能访问外部类的实例成员变量,反过来外部类不能访问对象声明中的成员变量;
- 伴生对象对于外部类相当于:一个静态成员变量(不能访问外部类的实例变量),反过来外部类可以访问伴生对象中定义的任何变量、方法;