Kotlin笔记 和Java不同的写法标记

扔物线老师的原文章_那些不是那么写的
跟着走一遍,加深印象和理解

构造器

  • 需要加 constructor 标识
  • 没有public修饰, 默认可见性就是公开的
class User {
    var name: String = ""
    constructor(name: String) {
        this.name = name
    }
}

init

  • 都在实例化时执行,并且执行顺序都在构造器之前
java 
public class User {
   👇
    {
        // 初始化代码块,先于下面的构造器执行
    }
    public User() {
    }
}
🏝️
class User {
    👇
    init {
        // 初始化代码块,先于下面的构造器执行
    }
    constructor() {
    }
}

final

image.png
  • kotlin 中的 val 和 final类似
  • kotlin 函数参数默认是val类型, 所以参数钱不需要写 val 关键字

val自定义getter

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

val size: Int
    get() { // 👈 每次获取 size 值时都会执行 items.size
        return items.size
    }

static property /function

  • 在 Kotlin 里,静态变量和静态方法这两个概念被去除了
    companion object
class Sample {
    ...
       👇
    companion object {
        val anotherString = "Another String"
    }
}

object (kotlin的单例)

  • Java 中的 Object在 Kotlin 中变成了 Any,和 Object作用一样:作为所有类的基类。
  • object不是类, 在kotlin中是和calss一样属于关键字
  • object的意思: 创建一个类,并且创建一个这个类的对象,在代码中使用这个对象,可以直接通过类名访问,
  • 创建一个kotlin的单例, 只需要将class换成object
  • object 继承和实现接口都可(简单来讲, 将class关键字的功能和单例合二为一)

和java相比不同点:

  • 和类的定义类似,但是把 class 换成了 object 。
  • 不需要额外维护一个实例变量 sInstance。
  • 不需要「保证实例只创建一次」的 getInstance() 方法。

匿名类

Java的写法 👇

ViewPager.SimpleOnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() {
    @Override // 👈
    public void onPageSelected(int position) {
        // override
    }
};

Kotlin的写法 👇

val listener = object: ViewPager.SimpleOnPageChangeListener() {
   override fun onPageSelected(position: Int) {
       // override
   }

}
Kotlin

  • 和java很相似, 只是将new换成了object
  • Java 中 new 用来创建一个匿名类的对象
  • Kotlin 中 object: 也可以用来创建匿名类的对象
  • 这里的 newobject:修饰的都是接口或者抽象类。

companion object

  • object修饰对象中的变量函数都是静态的
    只想让类中的一部分函数和变量是静态,写法👇
class A {
    object B {
        var c: Int = 0
    }
}

访问可以直接 A.B.c

类中嵌套的对象可以用 companion 修饰

  • 当有 companion 修饰时,对象的名字也可以省略掉


    一个类中只能有一个伴生对象

top-level property / function 声明

top-level declaration 顶层声明」

  • 其实就是把属性和函数的声明不写在 class 里面
package com.example.kotlinstudy

fun topTest(){
}

引用topTest()

  • 这样写的属性和函数,不属于任何class,直接属于 package
  • 和静态变量,静态函数一样是全局的,使用的时候可不写类名
  • 相同命名的顶级函数, IDE会自动加上包前缀区分

objectcompanion objecttop-level 选择对比

  • 如果想写工具类的功能,直接创建文件,写 top-level「顶层」函数。
  • 如果需要继承别的类或者实现接口,就用 object 或 companion object。

常 量

class Sample {
    companion object {
         👇                  // 👇
        const val CONST_NUMBER = 1
    }
}

数组和集合

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

Kotlin写法
val strs: Array<String> = arrayOf("a", "b", "c")
//获取: 
strs[0]
//修改: 
strs[1]="B"

kotlin中的数组是一个拥有泛型的类,创建函数也是泛型函数,和集合数据类型一样
kotlin数组的工具函数
get() / set()
contains()
first()
find()

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

集合

Kotlin 和 Java 一样有三种集合类型:List、Set 和 Map

  • list 以固定顺序存储一组元素,元素可以重复。
  • Set 存储一组互不相等的元素,通常没有固定顺序。
  • Map存储 键-值 对的数据集合,键互不相等,但不同的键可以对应相同的值。

list

//java 写法
List<String> strList = new ArrayList<>();
strList.add("a");
//kotlin写法
val strList = listOf("a", "b", "c")

Kotlin中的List支持协变,可把子类的list赋值给父类的list变量

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

而这在java中是会报错的

  • 对于协变的支持与否,List 和数组刚好反过来

和数组的区别

  • 这个问题在 Java 中就存在了,数组和 List 的功能类似,List 的功能更多一些,直觉上应该用 List 。但数组也不是没有优势,基本类型 (int[]、float[]) 的数组不用自动装箱,性能好一点。

  • 在 Kotlin 中也是同样的道理,在一些性能需求比较苛刻的场景,并且元素类型是基本类型时,用数组好一点。不过这里要注意一点,Kotlin 中要用专门的基本类型数组类 (IntArray FloatArray LongArray) 才可以免于装箱。也就是说元素不是基本类型时,相比 Array,用 List 更方便些。

Set

//java写法
Set<String> strSet = new HashSet<>();
strSet.add("a");
//Kotlin写法
val strSet = setOf("a", "b", "c")
  • 和 List 类似,Set 同样具有 covariant(协变)特性。

Map

//java写法
Map<String, Integer> map = new HashMap<>();
map.put("key1", 1);
//kotlin写法
val map=mapOf("key1" to 1, "key2" to 2,"key3" to 3)

mapOf的每个参数表示一个键值对,to表示将「键」和「值」关联,这个叫做「中缀表达式」

取值和修改

  • kotlin中的Map除了和 Java 一样可以使用 get() 根据键获取对应的值,还可以使用方括号的方式获取:
val value1 = map.get("key1")
val value2 = map["key2"]
  • 类似的,kotlin中也可以用方括号方式改变Map 键对应的值
val map = mutableMapOf("key1" to 1, "key2" to 2)
map.put("key1", 2)
map["key1"] = 2    
  • 你这里用到了「操作符重载」的知识,实现了和数组一样的「Positional Access Operations」

可变集合/不可变集合

上面修改Map值的例子中,创建函数用的是 mutableMapOf() 而不是 mapOf(),因为只有 mutableMapOf()创建的 Map 才可以修改。Kotlin 中集合分为两种类型:
只读的和可变的。这里的只读有两层意思:

  • 集合的size不可变
  • 集合中的元素值不可变

以下是三种集合类型创建不可变和可变实例的例子:
listOf()创建不可变的 ListmutableListOf()创建可变的 List
setOf()创建不可变的 SetmutableSetOf()创建可变的 Set
mapOf()创建不可变的 MapmutableMapOf()创建可变的 Map
mutable(单词意思可变的)前缀创建的函数都是可变的, 没有mutable前缀都不可变, 但是可以通过toMutable*()系函数转换成可变的集合

val strList = listOf("a", "b", "c")
strList.toMutableList() //转换成可变
val strSet = setOf("a", "b", "c")
strSet.toMutableSet() //转换成可变
val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3)
map.toMutableMap() //转换成可变
  • toMutable*() 返回的是一个新建的集合,原有的集合还是不可变的,所以只能对函数返回的集合修改。

Sequence

kotlin引入的新的容器类型 Sequence ,和 Iterable 一样用 来遍历一组数据并可以对每个元素进行特定的处理

创建

1. 类似listOf(),使用一组元素创建
sequenceOf("a", "b", "c")
2. 使用Iterable创建
val list=listOf("a", "b", "c")
list.asSequence()
3. 使用 lambda 表达式创建
val sequence= generateSequence(0) {it +1}

可见性修饰符

kotlin的四种可见性修饰符

  • public:公开,可见性最大,哪里都可以引用。
  • private:私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见。
  • protected:保护,相当于 private + 子类可见。
  • internal:内部,仅对 module 内可见。

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

public

Kotlin 中如果不写可见性修饰符,就表示公开,和 Java 中 public修饰符具有相同效果。在 Kotlin 中 public修饰符「可以加,但没必要」。

@ hide

在 Android 的官方 sdk 中,有一些方法只想对 sdk 内可见,不想开放给用户使用(因为这些方法不太稳定,在后续版本中很有可能会修改或删掉)。为了实现这个特性,会在方法的注释中添加一个 Javadoc 方法 @hide,用来限制客户端访问:

* @hide 👈
*/
public void hideMethod() {
    ...
}

但这种限制不太严格,可以通过反射访问到限制的方法。针对这个情况,Kotlin 引进了一个更为严格的可见性修饰符:internal

internal

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

  • Android Studio 里的 module
  • Maven project

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

Java 的「包内可见」怎么没了?

Java 的 default「包内可见」在 Kotlin 中被弃用掉了,Kotlin 中与它最接近的可见性修饰符是internal「module 内可见」。为什么会弃用掉包内可见呢?我觉得有这几个原因:

  • Kotlin 鼓励创建 top-level 函数和属性,一个源码文件可以包含多个类,使得 Kotlin 的源码结构更加扁平化,包结构不再像 Java 中那么重要。
  • 为了代码的解耦和可维护性,module 越来越多、越来越小,使得 internal 「module 内可见」已经可以满足对于代码封装的需求。

protected

  • Java 中 protected 表示包内可见 + 子类可见。
  • Kotlin 中 protected表示 private + 子类可见。

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

private

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

private修饰的变量「类中可见」和 「文件中可见」:

class Sample {
    private val propertyInClass = 1 // 👈 仅 Sample 类中可见
}

private val propertyInFile = "A string." // 👈 范围更大,整个文件可见
private 修饰内部类的变量时,在 Java 和 Kotlin 中的区别:
  • 在 Java 中,外部类可以访问内部类的 private 变量:
//java代码示例
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 变量:
//kotlin示例代码
class Outter {
    fun method() {
        val inner = Inner()
                            👇
        val result = inner.number * 2 // compile-error: Cannot access 'number': it is private in 'Inner'
    }
    
    class Inner {
        private val number = 1
    }
}
  • 可以修饰类和接口
  • 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阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容