kotlin - object关键字的使用
kotlin中的object关键字主要有如下三种用途:
对象表达式
对象表达式是完全取代java的匿名内部类的,并且它可以指定多个父类型,和java的匿名内部类还是有区别的,java的匿名内部类只能实现一个父类型。 对象表达式的语法如下:
object:[0-N接口或类]{
}
使用示例代码如下:
interface OnClickListener {
fun onclick()
}
abstract class View {
abstract fun draw()
}
fun main(args: Array<String>) {
val view = object : View(), OnClickListener {
override fun onclick() {
println("点击了----")
}
override fun draw() {
println("绘制")
}
}
view.onclick()
view.draw()
}
上面的程序反编译后java生成代码如下:
public final class ObjectDemoKt {
public static final void main(@NotNull String[] args) {
<undefinedtype> view = new OnClickListener() {
...
};
view.onclick();
view.draw();
}
}
从反编译后的生成的java代码可以看出,程序在遇到对象表达式的时候就会立即创建对象。 对象表达式有以下规则:
- 对象表达式不能是抽象类,因为系统在创建对象表达式时会立即创建对象。
- 对象表达式不能有构造方法。但可以有初始化块。
- 对象表达式可以包含内部类(被inner修饰的类),但不能包含嵌套类。
示例代码如下:
fun main(args: Array<String>) {
var obj = object {
init {
println("初始化块")
}
var name = "kotlin"
fun test() {
println("test方法")
}
inner class Empty
}
println(obj.name)
obj.test()
}
上面的代码注释1处可以看出对象表达式没有指定父类型,这就是对象表达式的增强之处。对象表达式的本质其实就是增强版的匿名内部类。
kotlin中的对象表达式可以分为两种情况:
- 在方法或者函数的局部范围内,或使用private修饰的对象表达式,kotlin编译器可以识别它真实的类型,比如上面的程序。
- 非private修饰的对象表达式,编译器只会认为它是对父类或接口的处理。如果没有父类或接口系统认为它是Any类型。
示例代码如下:
class ObjectTypeDemo{
private val obj1 = object{
val name:String="fefefe"
}
internal val obj2 = object {
val name:String="fefefe"
}
private fun privateObj()=object {
val name:String="fefefe"
}
fun publicObj()=object {
val name:String="fefefe"
}
fun test(){
println(obj1.name)
println(obj2.name)
println(privateObj().name)
println(publicObj().name)
}
}
fun main(args: Array<String>) {
ObjectTypeDemo().test()
}
对象表达式和java的匿名内部类还有一点区别是,对象表达式可以访问和修改其他作用域的局部变量。而java中匿名内部类要访问其他作用域的变量,这个变量必须是final 的。 示例程序如下:
fun main(args: Array<String>) {
var a:Int=0
val obj3 = object {
fun chang(){
a++
println("a = $a")
}
}
obj3.chang()
}
对象表达式比java的匿名内部类增强了以下三个方面:
- 对象表达式可以指定多个父类型
- kotlin编译器能更准确的识别局部范围内或private对象表达式的类型
- 对象表达式可以访问和修改其所在范围内的局部变量。
对象声明
对象声明的语法格式如下:
object ObjectName:类型1,类型2,...,类型N{
}
示例代码如下:
object ObjectDeclaration{
val name:String="declaration"
fun test(){
println("test方法")
}
class fefe{
}
}
反编译后对象的java代码:
....
private ObjectDeclaration() {
}
static {
ObjectDeclaration var0 = new ObjectDeclaration();
INSTANCE = var0;
name = "declaration";
}
.......
从上面的java代码可以看出kotlin的对象声明的本质就是在静态代码块中创建一个本类对象的实例。在使用的时候其实都是通过实例调用的,同时这也是一种单例模式,在类初始化的时候就会创建一个实例。在使用的时候可以通过类名.成员 来访问
对象表达式与对象声明的区别:
- 对象表达式可以赋值给变量,对象声明不可以。
- 对象表达式可以包含内部类,不可以包含嵌套类,而对象声明可以包含嵌套类但不能包含内部类。
- 对象表达式可以定义在方法或函数中,对象声明不可以。
伴生对象和静态成员
在类中定义的对象声明,可以用companion修饰,是它成为伴生对象。 kotlin中的伴生对象的主要目的就是解决kotlin取消了static关键字的问题。 示例代码如下:
class MyClass{
companion object{
val name :String="伴生对象"
fun test(){
println("test方法")
}
}
}
fun main(args: Array<String>) {
println(MyClass.name)
MyClass.test()
println(MyClass.Companion)
}
由于我们是通过类名.成员 的方式来访问伴生对象的成员的,所以伴生对象的名称也没什么意义,可以省略。如果我们要访问伴生对象可以通过注释1处的代码来访问。 伴生对象主要作用是为它所在的类模拟静态成员,但只是模拟,实际上伴生对象的成员还是属于伴生对象,并不属于它所在的外部类。 反编译以上代码所对应的java代码如下:
public final class MyClass {
@NotNull
private static final String name = "伴生对象";
@NotNull
public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);
...
public static final class Companion {
@NotNull
public final String getName() {
return MyClass.name;
}
public final void test() {
String var1 = "test方法";
boolean var2 = false;
System.out.println(var1);
}
private Companion() {
}
...
}
}
从上面的java代码可以看出确实伴生对象的成员并不是静态的也不属于Myclass的成员。 要想把伴生对象的成员为其所在的外部类生成对象的静态成员,可以使用@JvmStatic这个注解来实现。代码如下:
class MyClass{
companion object{
@JvmStatic
val name :String="伴生对象"
@JvmStatic
fun test(){
println("test方法")
}
}
}
反编译以后java代码如下:
public final class MyClass {
@NotNull
private static final String name = "伴生对象";
@NotNull
public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);
@NotNull
public static final String getName() {
MyClass.Companion var10000 = Companion;
return name;
}
@JvmStatic
public static final void test() {
Companion.test();
}
....
}
可以看出确实是在外部类下生成了对应的静态成员。
|