1. 类的构造器 & init
代码块
1.1 主构造器 & 副构造器在使用时的注意事项 & 注解 @JvmOverloads
-
推荐在类定义时为类提供一个主构造器;
-
在为类提供了主构造器的情况下,当再定义其他的副构造器时,要求副构造器必须调用到主构造器,否则报语法错误;
-
在继承时,如果父类中定义了主构造器或者副构造器,那么子类在继承父类时要在子类的构造器后面指明所调用的父类构器;
-
可以在定义构造器时为形参提供默认参数值;
-
如果要在
Java
中调用Kotlin
类的带默认参数值的构造器,需要使用注解@JvmOverloads
修饰Kotlin
类的带默认参数值构造器。此时就相当于Java
把带默认参数值的构造器分解成多个重载的构造器。
1.2 init
代码块
-
init{...}
代码块相当于Java
类中的非静态代码块{...}
。 -
二者都会在实例化对象的过程中执行,区别是
init{...}
代码块中可以访问到主构造器的形参。 -
先执行
init{...}
代码块,再执行副构造器的函数体。 -
Kotlin
类的成员属性必须进行初始化。可以在定义时就初始化;也可以放到init{...}
代码块中初始化。 -
init{...}
代码块和Java
类中的非静态代码块{...}
一样,都可以定义多个。
1.3 示例 1:init
代码块 & 主/副构造器的使用
1.4 示例 2:在声明继承关系的同时指定所调用的父类构造器
1.5 示例 3:带默认参数的构造器 & 注解 @JvmOverloads
的使用场景
2. 使用与类同名的工厂函数创建类对象
在 Kotlin
中可以定义一个全局的与某个类同名的函数作为 工厂函数 来实例化这个类。从外观上看,就好像是重载了类的构造器一样。
这样做的好处是:对于无法被继承的类,可以通过定义一个这样的同名的全局函数来实例化这个类,从而能够加上一些我们自己的业务代码。
示例:
3. 访问权限修饰符
3.1 Java
与 Kotlin
的访问权限修饰符对比
Java |
Kotlin |
|
---|---|---|
public |
公开 | 公开,是 Kotlin 中的默认权限 |
internal |
无 | 模块内可见 |
default |
包内可见,是 Java 中的默认权限 |
无 |
protected |
包内及子类可见 | 子类可见 |
private |
仅类内可见 | 文件内及类内可见 |
3.2 Kotlin
中的访问权限修饰符的作用对象
顶级声明 | 类 | 成员 | |
---|---|---|---|
public |
√ | √ | √ |
internal |
√ | √ | √ |
protected |
× | × | √ |
private |
√ | √ | √ |
3.3 Kotlin
中模块的概念
IntelliJ IDEA
工程中的一个 Module
可以看成是一个 Kotlin
模块。
也就是说:一个 jar
包;一个 aar
库,都可以看成是一个 Kotlin
模块。
3.4 Kotlin
中修饰符 internal
& Java
中修饰符 default
3.4.1 Java
中修饰符 default
的使用场景 & 缺陷
在封装 SDK
或对外提供公共组件时,对于 SDK
或公共组件中的核心类,若我们不想让使用者访问到,通常会使用 default
修饰,即包内可见。
但是,如果使用者一定要访问这些核心类的话,也可以创建一个与核心类同包名的包路径,将预访问这些核心类的外部类定义在同包名的包路径下即可。
也就是说,Java
中的 default
权限修饰符是存在缺陷的:
-
我们在封装
SDK
或封装公共组件时,无法通过default
修饰符将核心类给彻底的对外隐藏起来。 -
并且,对于
default
修饰的多个核心类,如果它们之间要相互访问,那么也必须放到同一个包路径下,即包名要相同。这就意味着即使这些核心类可分为不同的功能模块、不同的抽象层次,我们也无法按照功能或抽象层次不同将它们分别放到不同的包路径下。从而导致了同一个包路径下聚集了大量的功能不同、抽象层次不同的类文件,使工程结构变得臃肿、不明确、难以维护。
3.4.2 Kotlin
中使用修饰符 internal
解决 Java
中修饰符 default
的缺陷
在 Kotlin
中使用 internal
修饰符可以解决 Java
中的 default
修饰符的缺陷:
-
被
internal
修饰的核心类或成员只是在模块内可见。这意味着我们在一个模块中封装的SDK
或公共组件,在提供给其他模块使用时,在其他模块中是无法访问到SDK
或公共组件中被internal
修饰的核心类或成员的。 -
并且,在模块中被
internal
修饰符的核心类并没有限制都要放在同一个包路径中才能相互访问(只要它们在同一个模块中就可以相互访问)。所以,对于可分为不同功能模块、不同抽象层次的各个核心类,可以放在同一个模块下的不同包路径中。保证了工程结构清晰明确。
3.4.3 修饰符 internal
在 Java
中无效 & 注解 @JvmName
的作用
Kotlin
中的 internal
修饰符对 Java
是无效的。也就是说,在其他模块的 Java
代码中是可以访问到 SDK
或公共组件模块中被 internal
修饰的核心类或成员的。
需要指出的是:在其他模块的 Java
中访问 Kotlin
中被 internal
修饰的成员方法时,默认会为该成员方法的方法名添加额外的后缀 “$moduleName_buildType
”。
如果不想使用这种添加了后缀的默认方法名,我们可以使用注解 @JvmName
声明在 Java
中调用时的方法名称。
特别地:如果我们不想让其他模块中的 Java
代码能够访问到被 internal
修饰的 Kotlin
中的成员方法,那么可以使用注解 @JvmName
为这个成员方法声明一个 不合法的方法名称(如声明一个非字母或下划线开头的方法名称),于是 Java
中在调用这个不合法的方法名时就会报语法错误。
示例:
3.5 为构造器添加访问权限修饰符
当为 Kotlin
类的主构造器添加访问权限修饰符时,不能省略主构造器中的关键字 constructor
。
当为 Kotlin
类的副构造器添加访问权限修饰时,直接在构造器定义的最前面加修饰符即可。
3.6 为 Kotlin
类的属性添加访问权限修饰符
可以在主构造器中定义成员属性时直接为该属性声明访问权限修饰符。
对于属性的 setter
/getter
方法:
getter
方法的访问权限修饰符必须同属性的访问权限修饰符一致;setter
方法的访问权限修饰符的权限不得超过属性的访问权限修饰符。
3.7 什么是顶级声明 & 为顶级声明添加访问权限修饰符
顶级声明是指:文件中定义的全局变量、全局函数、类。
顶级声明不能被 protected
修饰。
顶级声明被 private
修饰时,表示仅当前文件内可见。
4. 属性的延迟初始化
4.1 Kotlin 中属性必须赋初值 & 特殊情况下的延迟初始化
一般情况下,Kotlin
要求类属性必须赋以初始化值,具体分为两种情形:
- 在定义属性时赋以初始化值;
- 在
init
代码块中赋以初始化值。