static 去哪了
在我学 Kotlin 第一天的时候就发现了一个问题,我们学习 Java 的时候,第一个示例输出Hello world的时候,main方法总是会被public static void 修饰着,而 Kotlin只需要简单的 fun main(),前面的我们知道 void 在 Kotlin 中可以用 Uint取代,那么static,Kotlin 使用什么魔法呢? 那就是object,他可以完美的代替static,下面我们来了解一下object。
伴生对象
按照惯例,我们还是从代码开始
public class People {
private String name;
private int age;
private int sex;
public People(String name, int age, int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
static boolean isMale(People people) {
return people.sex == SEX_MALE;
}
static int SEX_MALE = 0;
static int SEX_FEMALE = 1;
public static void main(String[] args) {
People people = new People("lihong", 12, People.SEX_FEMALE);
System.out.println(People.isMale(people));
}
}
上面的代码在 Java 中比较常见,当然也有可能存在包装一个工具类来判断性别等等。但是总得来说,如果我们细看上面的代码,我们发现,这个类中,即有静态变量,也有静态方法,还有普通变量以及普通方法。放在一个类中,感觉挺混乱的。因为静态变量静态方法是属于这个类的,普通变量,普通方法是属于一个对象的。在 Kotlin 中为了让这种结构清晰,于是就引入了伴生对象。而伴生对象其实就是相对于这个类来说的。意思就是伴随这个类的对象。当类被加载的时候,就会初始化。而这里和 static 修饰的对象不谋而合。那么上面的代码使用 Kotlin 中伴生对象的方式来写,就是下面的代码:
class People(var name: String, var age: Int, val sex: Int) {
companion object {
val SEX_MALE = 0
val SEX_FEMALE = 1
fun isMale(people: People): Boolean {
return people.sex == SEX_MALE
}
}
}
fun main() {
val people = People("lihua", 12, People.SEX_FEMALE)
println(People.isMale(people))
}
我们再看下反编译后的代码
public final class People {
@NotNull
private String name;
private int age;
private final int sex;
private static final int SEX_MALE;
private static final int SEX_FEMALE = 1;
@NotNull
public static final Companion Companion = new Companion((DefaultConstructorMarker)null);
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public final int getSex() {
return this.sex;
}
public People(@NotNull String name, int age, int sex) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public static final class Companion {
public final int getSEX_MALE() {
return People.SEX_MALE;
}
public final int getSEX_FEMALE() {
return People.SEX_FEMALE;
}
public final boolean isMale(@NotNull People people) {
Intrinsics.checkNotNullParameter(people, "people");
return people.getSex() == ((Companion)this).getSEX_MALE();
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
public final class PeopleKt {
public static final void main() {
People people = new People("lihua", 12, People.Companion.getSEX_FEMALE());
boolean var1 = People.Companion.isMale(people);
System.out.println(var1);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
反编译后的代码我们看到,其实 companion object 修饰的部分,被编译撑了一个静态内部类,类名就是Companion。里面有两个get 方法,还有一个就是我们自己写的isMale方法,最后输出的也是 Companion 对象里面的返回值。所以可以看成,伴生对象就是用来替代 static 的一直方式。一个类的伴生对象全局只能有一个,因为反编译我们看到了其实就是一个静态类。所以这个我们很容易就想到了一种设计模式–单例模式。
Kotlin 单例
没错,上面我们了解到一个类的伴生对象全局只有一个,使用 companion object 修饰,自然而然的想到单例模式,Kotlin 的单例没错就是使用 object,我们看下 Kotlin 怎么创建一个单例的,我们以封装 LogUtil为例:
object LogUtil{
val packageName:String = "com.onexiao.log"
var tag:String = "TAG"
fun error(message:String){
println("[PKG]: $packageName , [MSG] $message")
}
...
}
反编译后我们看到,这里的单例模式其实就是饿汉式的,这里就不详细展开说明各种单例的创建方式,因为只是讲object 作用,所以仅说明一般场景的单例。
public final class LogUtil {
@NotNull
private static final String packageName;
@NotNull
private static String tag;
@NotNull
public static final LogUtil INSTANCE;
@NotNull
public final String getPackageName() {
return packageName;
}
@NotNull
public final String getTag() {
return tag;
}
public final void setTag(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
tag = var1;
}
public final void error(@NotNull String message) {
Intrinsics.checkNotNullParameter(message, "message");
String var2 = "[PKG]: " + packageName + " , [MSG] " + message;
System.out.println(var2);
}
private LogUtil() {
}
static {
LogUtil var0 = new LogUtil();
INSTANCE = var0;
packageName = "com.onexiao.log";
tag = "TAG";
}
}
object 表达式
作为Android 开发人员,对于各种 click 监听的写法可谓是深恶痛绝,匿名内部类在 Java 的写法也是,如今支持了 lambda 表达式还好,在 Kotlin 中,如果使用匿名内部类的方式,可以使用 object 表达式方式来,我们以 Android 常见的设置监听的方式
val clickListener = object :OnClickListener{
override fun onClick(v: View?) {
// TODO("Not yet implemented")
}
}
mft.setOnClickListener(clickListener)
当然这里仅仅举一个例子来说,如果真的想要简洁方式的话,直接使用 lambda 表达式,会更加简洁,这里只是用来说明 object 表达式,可以很好的将内部类代替。这里说明一下,如果匿名内部类里面的方法较多,比较适合使用 object 表达式,而方法只有一个的话,推荐使用 Lamba 表达式