[TOC]
## **顶层声明的可见性**
如果顶层声明是 private 的,它是声明它的文件所私有的(参见 可见性修饰符)。
### **[可见性修饰符](http://www.kotlincn.net/docs/reference/visibility-modifiers.html)**
类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有可见性修饰符(getter总是与属性有着相同的可见性)。 在 Kotlin 中有这**四个可见性修饰符:private、protected、internal 和 public** 。 如果没有显式指定修饰符的话,默认可见性是public 。
下面将根据声明作用域的不同来解释。
**包名**
函数、属性和类、对象和接口可以在顶层声明,即直接在包内:
~~~
// 文件名:example.kt
package foo
fun baz() {}
class Bar {}
~~~
* 如果你**不指定任何可见性修饰符,默认为 public**,这意味着你的声明将随处可见;
* 如果你声明为 **private** ,它**只会在声明它的文件内可见**;
* 如果你声明为 **internal** ,它**会在相同模块内随处可见**;
* **protected 不适用于顶层声明**。
比如:
~~~
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
private set // setter 只在 example.kt 内可见
internal val baz = 6 // 相同模块内可见
~~~
**类和接口**
对于类内部声明的成员:
* private 意味着只在这个类内部(包含其所有成员)可见;
* protected —— 和 private 一样 + 在子类中可见。
* internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员;
* public —— 能见到类声明的任何客户端都可见其 public 成员。
>[warning] **注意** :对于Java用户:Kotlin 中外部类不能访问内部类的 private 成员。
如果你覆盖一个 protected 成员并且没有显式指定其可见性,该成员还会是 protected 可见性。
例子:
~~~
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // 默认 public
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a 不可见
// b、c、d 可见
// Nested 和 e 可见
override val b = 5 // “b”为 protected
}
class Unrelated(o: Outer) {
// o.a、o.b 不可见
// o.c 和 o.d 可见(相同模块)
// Outer.Nested 不可见,Nested::e 也不可见
}
~~~
**构造函数**
要指定一个类的的主构造函数的可见性,使用以下语法(注意你**需要添加一个显式constructor 关键字**):
~~~
class C private constructor(a: Int) { …… }
~~~
这里的构造函数是私有的。**默认情况下,所有构造函数都是 public** ,这实际上等于类可见的地方它就可见(即 一个 internal 类的构造函数只能 在相同模块内可见).
**局部声明**
局部变量、函数和类不能有可见性修饰符。
**模块**
可见性修饰符 internal 意味着该成员只在相同模块内可见。更具体地说, 一个模块是编译在一起的一套 Kotlin 文件:
* 一个 IntelliJ IDEA 模块;
* 一个 Maven 或者 Gradle 项目;
* 一次 <kotlinc> Ant 任务执行所编译的一套文件。
### 定义函数
Kotlin中函数的结构是怎样的呢?
![](http://upload-images.jianshu.io/upload_images/7368752-7c934d7214fe0662.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* **带有两个 Int 参数、返回 Int 的函数**:
~~~
fun sum(a: Int, b: Int): Int {
return a + b
}
fun main(args: Array<String>) {
print("sum of 3 and 5 is ")
println(sum(3, 5))
}
~~~
* **将表达式作为函数体、返回值类型自动推断的函数**:
~~~
fun sum(a: Int, b: Int) = a + b
fun main(args: Array<String>) {
println("sum of 19 and 23 is ${sum(19, 23)}")
}
~~~
完整代码如下
~~~
fun main(args: Array<String>) {
println("sum of 19 and 23 is ${sum(19, 23)}")
}
fun sum(a: Int, b: Int) = a + b //Kotlin可以根据函数里的参数类型推测函数的返回类型
~~~
输出结果:
~~~
sum of 19 and 23 is 42
~~~
从输出结果可以看出,**Kotlin还支持类似JavaScript的 $占位符操作**。
>[info] $是一种javascript定义的符号,比如说$(document),可以获得当前页面的上下文,就是一个变量名而已。
> 如果在jquery框架里面的话它代表jquery本身。
> 其它时候它只是一个变量名,仅此而已。代表着一个事物,可以代表函数,参数等
> 比如
```
var $ = function(id)
{
return document.getElementById(id);
};
```
那么现在$就代表一个函数了,直接`$('myDiv')`,就等同于`document.getElementById('myDiv')`;
* **函数返回无意义的值**:
~~~
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
fun main(args: Array<String>) {
printSum(-1, 8)
}
~~~
其中的
~~~
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
~~~
相当于Java的
~~~
public void printSum(int a, int b){
println("sum of "+ a + "and"+ b +"is"+ (a + b));
}
~~~
其实可以把Unit省略掉
* **Unit 返回类型可以省略**:
~~~
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
fun main(args: Array<String>) {
printSum(-1, 8)
}
~~~
>[info] **注意**:Java的void是没有返回值的,而Kotlin的Unit却有返回值,只是返回值 no meaningful(无意义)。
### **Kotlin号称的nullpointer检测**
其实是这样实现的:
~~~
fun parseInt(str: String): Int? {
// ...省略String to Int的转换代码
//Return null if str does not hold an integer
//当str不是integer类型的时候,函数将返回null
}
~~~
测试代码
~~~
fun parseInt(str: String): Int? {//返回类型 Int后面加上一个 ? 就表示函数有可能是返回null
return str.toIntOrNull() //这是Kotlin的Api
}
fun printProduct(arg1: String, arg2: String) {
val x = parseInt(arg1)
val y = parseInt(arg2)
// Using `x * y` yields error because they may hold nulls.
if (x != null && y != null) {
// x and y are automatically cast to non-nullable after nullcheck
println(x * y)
} else {
println("either '$arg1' or '$arg2' is not a number")
}
}
fun main(args: Array<String>) {
printProduct("6", "7")
printProduct("a", "7")
printProduct("a", "b")
}
~~~
运行结果
~~~
42
either 'a' or '7' is not a number
either 'a' or 'b' is not a number
~~~
从上面的代码,`str.toIntOrNull()`,我们点击,查看源码,得知`“Parses the string as an [Int] number and returns the result or `null` if the string is not a valid representation of a number.”`直译就是“将字符串解析为 [Int] 数字,如果该字符串不是数字的有效表示形式,则返回结果或 "null"。”
### **Kotlin的变量类型检查**:
代码如下
~~~
fun getStringLength(obj: Any): Int? {
if (obj is String) { //is相当于java里的instance of
// `obj` is automatically cast to `String` in this branch
return obj.length
}
// `obj` is still of type `Any` outside of the type-checked branch
return null
}
fun main(args: Array<String>) {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
}
~~~
运行结果
~~~
'Incomprehensibilities' string length is 21
'1000' string length is ... err, not a string
~~~
这里有必要讲解一下“`?:`”这个符号,
* **?.(安全调用符)**
先看一下这个 **?.(安全调用符)**,安全调用符的出现为了解决什么问题?可空类型变量不能直接使用,但是直接使用非空判断又过于复杂,所以可以使用安全调用符。
怎么使用安全调用符?之前的结构是【变量.方法】,现在的结构是【**变量?.方法**】。
使用了安全调用符,代码执行逻辑是怎样的?变量不会NULL的时候,才去执行方法,所以不会报空指针。变量为NULL的时候,【变量?.方法】的结果为NULL。
![](http://upload-images.jianshu.io/upload_images/7368752-52651a80c6cb6723.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* **?:( Elvis操作符)**
针对【变量?.方法】,如果变量为NULL,【变量?.方法】的返回结果是NULL,那我们能不能**指定想返回的值**呢?答案是肯定的,我们需要配合Elvis操作符,使用方式为【变量?:值 】
![](http://upload-images.jianshu.io/upload_images/7368752-338762cd48b16b00.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 前言
- Kotlin简介
- IntelliJ IDEA技巧总结
- idea设置类注释和方法注释模板
- 像Android Studion一样创建工程
- Gradle
- Gradle入门
- Gradle进阶
- 使用Gradle创建一个Kotlin工程
- 环境搭建
- Androidstudio平台搭建
- Eclipse的Kotlin环境配置
- 使用IntelliJ IDEA
- Kotlin学习路线
- Kotlin官方中文版文档教程
- 概述
- kotlin用于服务器端开发
- kotlin用于Android开发
- kotlin用于JavaScript开发
- kotlin用于原生开发
- Kotlin 用于数据科学
- 协程
- 多平台
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 开始
- 基本语法
- 习惯用法
- 编码规范
- 基础
- 基本类型
- 包与导入
- 控制流
- 返回与跳转
- 类与对象
- 类与继承
- 属性与字段
- 接口
- 可见性修饰符
- 扩展
- 数据类
- 密封类
- 泛型
- 嵌套类
- 枚举类
- 对象
- 类型别名
- 内嵌类
- 委托
- 委托属性
- 函数与Lambda表达式
- 函数
- Lambda表达式
- 内联函数
- 集合
- 集合概述
- 构造集合
- 迭代器
- 区间与数列
- 序列
- 操作概述
- 转换
- 过滤
- 加减操作符
- 分组
- 取集合的一部分
- 取单个元素
- 排序
- 聚合操作
- 集合写操作
- List相关操作
- Set相关操作
- Map相关操作
- 多平台程序设计
- 平台相关声明
- 以Gradle创建
- 更多语言结构
- 解构声明
- 类型检测与转换
- This表达式
- 相等性
- 操作符重载
- 空安全
- 异常
- 注解
- 反射
- 作用域函数
- 类型安全的构造器
- Opt-in Requirements
- 核心库
- 标准库
- kotlin.test
- 参考
- 关键字与操作符
- 语法
- 编码风格约定
- Java互操作
- Kotlin中调用Java
- Java中调用Kotlin
- JavaScript
- 动态类型
- kotlin中调用JavaScript
- JavaScript中调用kotlin
- JavaScript模块
- JavaScript反射
- JavaScript DCE
- 原生
- 并发
- 不可变性
- kotlin库
- 平台库
- 与C语言互操作
- 与Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 调试
- FAQ
- 协程
- 协程指南
- 基础
- 取消与超时
- 组合挂起函数
- 协程上下文与调度器
- 异步流
- 通道
- 异常处理与监督
- 共享的可变状态与并发
- Select表达式(实验性)
- 工具
- 编写kotlin代码文档
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin与OSGI
- 编译器插件
- 编码规范
- 演进
- kotlin语言演进
- 不同组件的稳定性
- kotlin1.3的兼容性指南
- 常见问题
- FAQ
- 与Java比较
- 与Scala比较(官方已删除)
- Google开发者官网简介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin样品
- Kotlin零基础到进阶
- 第一阶段兴趣入门
- kotlin简介和学习方法
- 数据类型和类型系统
- 入门
- 分类
- val和var
- 二进制基础
- 基础
- 基本语法
- 包
- 示例
- 编码规范
- 代码注释
- 异常
- 根类型“Any”
- Any? 可空类型
- 可空性的实现原理
- kotlin.Unit类型
- kotlin.Nothing类型
- 基本数据类型
- 数值类型
- 布尔类型
- 字符型
- 位运算符
- 变量和常量
- 语法和运算符
- 关键字
- 硬关键字
- 软关键字
- 修饰符关键字
- 特殊标识符
- 操作符和特殊符号
- 算术运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- this关键字
- super关键字
- 操作符重载
- 一元操作符
- 二元操作符
- 字符串
- 字符串介绍和属性
- 字符串常见方法操作
- 字符串模板
- 数组
- 数组介绍创建及遍历
- 数组常见方法和属性
- 数组变化以及下标越界问题
- 原生数组类型
- 区间
- 正向区间
- 逆向区间
- 步长
- 类型检测与类型转换
- is、!is、as、as-运算符
- 空安全
- 可空类型变量
- 安全调用符
- 非空断言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函数
- 函数式编程概述
- OOP和FOP
- 函数式编程基本特性
- 组合与范畴
- 在Kotlin中使用函数式编程
- 函数入门
- 函数作用域
- 函数加强
- 命名参数
- 默认参数
- 可变参数
- 表达式函数体
- 顶层、嵌套、中缀函数
- 尾递归函数优化
- 函数重载
- 控制流
- if表达式
- when表达式
- for循环
- while循环
- 循环中的 Break 与 continue
- return返回
- 标签处返回
- 集合
- list集合
- list集合介绍和操作
- list常见方法和属性
- list集合变化和下标越界
- set集合
- set集合介绍和常见操作
- set集合常见方法和属性
- set集合变换和下标越界
- map集合
- map集合介绍和常见操作
- map集合常见方法和属性
- map集合变换
- 集合的函数式API
- map函数
- filter函数
- “ all ”“ any ”“ count ”和“ find ”:对集合应用判断式
- 别样的求和方式:sumBy、sum、fold、reduce
- 根据人的性别进行分组:groupBy
- 扁平化——处理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 区间、数组、集合之间转换
- 面向对象
- 面向对象-封装
- 类的创建及属性方法访问
- 类属性和字段
- 构造器
- 嵌套类(内部类)
- 枚举类
- 枚举类遍历&枚举常量常用属性
- 数据类
- 密封类
- 印章类(密封类)
- 面向对象-继承
- 类的继承
- 面向对象-多态
- 抽象类
- 接口
- 接口和抽象类的区别
- 面向对象-深入
- 扩展
- 扩展:为别的类添加方法、属性
- Android中的扩展应用
- 优化Snackbar
- 用扩展函数封装Utils
- 解决烦人的findViewById
- 扩展不是万能的
- 调度方式对扩展函数的影响
- 被滥用的扩展函数
- 委托
- 委托类
- 委托属性
- Kotlin5大内置委托
- Kotlin-Object关键字
- 单例模式
- 匿名类对象
- 伴生对象
- 作用域函数
- let函数
- run函数
- with函数
- apply函数
- also函数
- 标准库函数
- takeIf 与 takeUnless
- 第二阶段重点深入
- Lambda编程
- Lambda成员引用高阶函数
- 高阶函数
- 内联函数
- 泛型
- 泛型的分类
- 泛型约束
- 子类和子类型
- 协变与逆变
- 泛型擦除与实化类型
- 泛型类型参数
- 泛型的背后:类型擦除
- Java为什么无法声明一个泛型数组
- 向后兼容的罪
- 类型擦除的矛盾
- 使用内联函数获取泛型
- 打破泛型不变
- 一个支持协变的List
- 一个支持逆变的Comparator
- 协变和逆变
- 第三阶段难点突破
- 注解和反射
- 声明并应用注解
- DSL
- 协程
- 协程简介
- 协程的基本操作
- 协程取消
- 管道
- 慕课霍丙乾协程笔记
- Kotlin与Java互操作
- 在Kotlin中调用Java
- 在Java中调用Kotlin
- Kotlin与Java中的操作对比
- 第四阶段专题练习
- 朱凯Kotlin知识点总结
- Kotlin 基础
- Kotlin 的变量、函数和类型
- Kotlin 里那些「不是那么写的」
- Kotlin 里那些「更方便的」
- Kotlin 进阶
- Kotlin 的泛型
- Kotlin 的高阶函数、匿名函数和 Lambda 表达式
- Kotlin协程
- 初识
- 进阶
- 深入
- Kotlin 扩展
- 会写「18.dp」只是个入门——Kotlin 的扩展函数和扩展属性(Extension Functions / Properties)
- Kotlin实战-开发Android