ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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)