第二章 Java与Kotlin的写法比较

3 Java与Kotlin的写法比较

3.1 构造器、变量、常量和静态数据

3.1.1 构造函数

java中的构造函数是函数,和类的名称相同,没有返回类型。

```

public class User {

    int id;

    String name;

    public User(int id, String name) {

        this.id = id;

        this.name = name;

    }

}

```

kotlin中的则是使用constructor这个关键字。

```

class User {

    val id: Int

    val name: String

    constructor(id: Int, name: String) {

        this.id = id

        this.name = name

    }

}

```

可以发现有两点不同: 

Java 中构造器和类同名,Kotlin 中使用 constructor 表示。 

Kotlin 中构造器没有 public 修饰,因为默认可见性就是公开的

3.1.2 初始化代码块

在遇到一些需要在构造方法执行之前执行的情形时,我们通常使用初始化代码块。

在java中,只需要将代码块用{}包起来,置于构造函数之前就可以,而kotlin的写法大致类似,不同的是需要在{}之前加上init关键字。

```

public class User {

    {

        // 初始化代码块,先于下面的构造器执行

    }

    public User() {

    }

}

```

```

class User {

    init {

        // 初始化代码块,先于下面的构造器执行

    }

    constrouctor() {

    }

}

```

3.1.3 final

Kotlin 中的 val和 Java 中的 final类似,表示只读变量,不能修改。这里分别从成员变量、参数和局部变量来和 Java 做对比:

```

final int final1 = 1;

void method(final String final2) {

    final String final3 = "The parameter is " + final2;

}

```

```

val fina1 = 1

//参数是没有 val 的,在kotlin中函数参数是默认val的,不允许被修改,减少了出错率

fun method(final2: String) {

    val final3 = "The parameter is " + final2

}

```

可以看到不同点主要有: 

1.final 变成了 val。 

2.Kotlin 函数参数默认是 val 类型,所以参数前不需要写 val 关键字,Kotlin 里这样设计的原因是保证了参数不会被修改,而 Java 的参数可修改(默认没 final 修饰)会增加出错的概率。

不过 val 和 final 还是有一点区别的,虽然 val 修饰的变量不能二次赋值,但可以通过自定义变量的 getter 函数,让变量每次被访问时,返回动态获取的值:

```

val size: Int

    get() { //每次获取 size 值时都会执行 items.size

        return items.size

    }

```

不过这个属于 val 的另外一种用法,大部分情况下 val 还是对应于 Java 中的 final 使用的。

final在java中最被常用的地方,便是用来修饰常量,并且通常和static连用

```

public static final int CODE_ONE = 1;

```

在kotlin中,虽然final可以用val来修饰,但是static却没有直接的相对应的关键字。在kotlin中,是没有静态常量以及静态方法这两个概念的,若是想要像在java中直接类引用,就使用companion object

```

companion object{

    val codeOne : Int = 1; //静态常量


    fun getHH(a : Int,b : Int) : Int {

        return a*b

    }//此为静态方法

}

```

3.1.4 object

Kotlin 里的 object ——首字母小写的,不是大写,Java 里的 Object 在 Kotlin 里不用了。   

Java 中的 Object 在 Kotlin 中变成了 Any,和 Object 作用一样:作为所有类的基类。

而 object 不是类,像 class 一样在 Kotlin 中属于关键字: 

它的意思很直接:创建一个类,并且创建一个这个类的对象。这个就是 object 的意思:对象。

```

object Sample {

    val name = "A name"

}

```

在代码中如果要使用这个对象,直接通过它的类名就可以访问:

```

Sample.name

```

这就是java中的单例,不过kotlin创建单例的方法比java要简单许多。

Java 中实现单例类(非线程安全): 

```

public class A {

    private static A sInstance;


    public static A getInstance() {

        if (sInstance == null) {

            sInstance = new A();

        }

        return sInstance;

    }

    //还有很多模板代码

    ...

}

```

```

// class 替换成了 object

object A {

    val number: Int = 1

    fun method() {

        println("A.method()")

    }

```

和 Java 相比的不同点有: 

1.和类的定义类似,但是把 class 换成了 object 。

不需要额外维护一个实例变量 sInstance。

不需要「保证实例只创建一次」的 getInstance() 方法。 

2.这种通过 object 实现的单例是一个饿汉式的单例,并且实现了线程安全。

继承类和实现接口

Kotlin 中不仅类可以继承别的类,可以实现接口,object 也可以:

```

open class A {

    open fun method() {

        ...

    }

}

interface B {

    fun interfaceMethod()

}

object C : A(), B {

    override fun method() {

        ...

    }

    override fun interfaceMethod() {

        ...

    }

}

```

为什么 object 可以实现接口呢? 

简单来讲 object 其实是把两步合并成了一步,既有 class 关键字的功能,又实现了单例,这样就容易理解了。 

3.2 数组和集合

3.2.1 数组

声明一个 String 数组:

```

String[] strs = {"a", "b", "c"};

```

```

val strs: Array<String> = arrayOf("a", "b", "c")

```

可以看到 Kotlin 中的数组是一个拥有泛型的类,创建函数也是泛型函数,和集合数据类型一样。 

将数组泛型化有什么好处呢?对数组的操作可以像集合一样功能更强大,由于泛型化,Kotlin 可以给数组增加很多有用的工具函数: 

get() / set() 

contains() 

first() 

find()

这样数组的实用性就大大增加了。

取值和修改

Kotlin 中获取或者设置数组元素和 Java 一样,可以使用方括号加下标的方式索引:

```

println(strs[0])

strs[1] = "B"

```

不支持协变

Kotlin 的数组编译成字节码时使用的仍然是 Java 的数组,但在语言层面是泛型实现,这样会失去协变 (covariance) 特性,就是子类数组对象不能赋值给父类的数组变量:

```

val strs: Array<String> = arrayOf("a", "b", "c")

val anys: Array<Any> = strs // compile-error: Type mismatch

```

而这在 Java 中是可以的:

```

String[] strs = {"a", "b", "c"};

Object[] objs = strs; // success

```

3.2.2 集合

Kotlin 和 Java 一样有三种集合类型:List、Set 和 Map,它们的含义分别如下: 

1.List 以固定顺序存储一组元素,元素可以重复。   

2.Set 存储一组互不相等的元素,通常没有固定顺序。 

3.Map 存储 键-值 对的数据集合,键互不相等,但不同的键可以对应相同的值。

从 Java 到 Kotlin,这三种集合类型的使用有哪些变化呢?我们依次看看。 

List

Java 中创建一个列表:

```

List<String> strList = new ArrayList<>();

strList.add("a");

strList.add("b");

strList.add("c"); //添加元素繁琐

```

Kotlin 中创建一个列表:

```

val strList = listOf("a", "b", "c")

```

首先能看到的是 Kotlin 中创建一个 List 特别的简单,有点像创建数组的代码。而且 Kotlin 中的 List 多了一个特性:支持 covariant(协变)。也就是说,可以把子类的 List 赋值给父类的 List 变量: 

Kotlin:

```

val strs: List<String> = listOf("a", "b", "c")

val anys: List<Any> = strs // success

```

而这在 Java 中是会报错的:

```

List<String> strList = new ArrayList<>();

List<Object> objList = strList; //compile error: incompatible types

```

Sequence

这是在kotlin中新出现的集合,类似于java中的Iterable,可以用来遍历一组数据并可以对每个元素进行特定的处理。

类似 listOf() ,使用一组元素创建:

```

sequenceOf("a", "b", "c")

```

使用 Iterable 创建:

```

val list = listOf("a", "b", "c")

list.asSequence()

```

这里的 List 实现了 Iterable 接口。

使用 lambda 表达式创建:

```

//第一个元素

val sequence = generateSequence(0) { it + 1 }

//lambda 表达式,负责生成第二个及以后的元素,it 表示前一个元素

```

3.3 可见性修饰符

讲完了数据集合,再看看 Kotlin 中的可见性修饰符,Kotlin 中有四种可见性修饰符: 

public:公开,可见性最大,哪里都可以引用。 

private:私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见。 

protected:保护,相当于 private + 子类可见。 

internal:内部,仅对 module 内可见。

相比 Java 少了一个 default 「包内可见」修饰符,多了一个 internal「module 内可见」修饰符。这一节结合例子讲讲 Kotlin 这四种可见性修饰符,以及在 Kotlin 和 Java 中的不同。 

3.3.1 public

在Java中不写可见性修饰符的类,仅包内可见,即在package范围内调用。若是要在包之外的地方调用,便需要加上public修饰符。 

在kotlin中也有public,默认不写的可见性修饰就表示public,可见范围与java中的public一样。

Ps :注意,kotlin中的类不光是默认public的,更是默认final的,所以想继承该类,必须使用open关键字。

3.3.2 @hide

在 Android 的官方 sdk 中,有一些方法只想对 sdk 内可见,不想开放给用户使用(因为这些方法不太稳定,在后续版本中很有可能会修改或删掉)。 

为了实现这个特性,会在方法的注释中添加一个 Javadoc 方法 @hide,用来限制客户端访问:

```

/**

* @hide

*/

public void hideMethod() {

    ...

}

```

这种方法还是可以通过反射拿到的,针对此类情形,kotlin中有一个更加严格的可见性修饰符————internal。 

3.3.3 internal

internal 表示修饰的类、函数仅对 module 内可见,这里的 module 具体指的是一组共同编译的 kotlin 文件,常见的形式有:

Android Studio 里的 module

Maven project

我们常见的是 Android Studio 中的 module 这种情况,Maven project 仅作了解就好,不用细究。

internal 在写一个 library module 时非常有用,当需要创建一个函数仅开放给 module 内部使用,不想对 library 的使用者可见,这时就应该用  internal 可见性修饰符。 

3.3.4 protected

Java 中 protected 表示包内可见(default) + 子类可见。 

Kotlin 中 protected 表示 private + 子类可见。 

Kotlin 相比 Java protected 的可见范围收窄了,原因是 Kotlin 中不再有「包内可见」的概念了,相比 Java 的可见性着眼于 package,Kotlin 更关心的是 module。 

3.3.5 private

Java 中的 private 表示类中可见,作为内部类时对外部类「可见」。 

Kotlin 中的 private  表示类中或所在文件内可见,作为内部类时对外部类「不可见」。

```

class Sample {

    private val propertyInClass = 1 //仅 Sample 类中可见

}

private val propertyInFile = "A string." //范围更大,整个文件可见

```

java和kotlin中用private来修饰内部类中变量的的区别:

```

//在java中可以直接获取内部类的private类变量

public class Outter {

    public void method() {

        Inner inner = new Inner();

        int result = inner.number * 2; // success

    }

    private class Inner {

        private int number = 0;

    }

}

```

```

//在kotlin中不能直接获取内部类的private类变量

class Outter {

    function method() {

        val inner : Inner  = Inner();

        val result : Int = inner.number * 2;

        //compile-error: Cannot access 'number': it is private in 'Inner'

    }

    private class Inner {

        private val number : Int  = 0;

    }

}

```

可以修饰类和接口: 

Java 中一个文件只允许一个外部类,所以 class 和 interface 不允许设置为 private,因为声明 private 后无法被外部使用,这样就没有意义了。

Kotlin 允许同一个文件声明多个 class 和 top-level 的函数和属性,所以 Kotlin 中允许类和接口声明为 private,因为同个文件中的其它成员可以访问:

```

private class Sample {

    val number = 1

    fun method() {

        println("Sample method()")

    }

}

//在同一个文件中,所以可以访问

val sample = Sample()

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容