JGSK - Kotlin - 08.Utils
Utils
本章主要介绍一些 Kotlin 内置的工具类和方法。
本章主要介绍一些 Kotlin 内置的工具类和方法。
普通函数在执行时程序会跳转到函数定义处执行完毕后再返回调用处。而内联函数指的是函数定义被整个复制到了调用处,所以与普通函数相比,内联函数无需进行程序的跳转,有助于提高程序的运行速度。但是大量的内联函数会增大应用的体积,从而减慢应用的加载速度。
DSL 即 domain-specific languages,领域特定语言。和一般的编程语言不同,领域特定语言只能用于特定的领域中并且表现形式有限。领域特定语言最大的功能就是可以让语言本身更容易阅读,方便开发者和领域专家进行交流。
从 Java 1.8 开始 Java 也支持了集合的函数式编程。
Stream 是 Java 1.8 引入的类,主要用于进行 并行集合的流式操作。
集合可以使用调用 stream()
方法或者使用 Stream
类的静态方法来转换为 Stream
对象。
Memoized 即缓存功能。Groovy 可以对方法和闭包的结果进行缓存,从而再下次传入同样的参数时直接返回缓存的结果。对于那些耗时很久的计算过程来说这一点可以节约不少时间。
DSL 即 domain-specific languages,领域特定语言。和一般的编程语言不同,领域特定语言只能用于特定的领域中并且表现形式有限。领域特定语言最大的功能就是可以让语言本身更容易阅读,方便开发者和领域专家进行交流。
Scala 中的鸭子类型其实就是结构类型。通过使用鸭子类型可以限定方法的参数只需要包含某种结构就行,而无需像 Java 一样为所有能传入方法的类型定义一个统一的父类。
Actor 是 Scala 的并发模型。在 2.10 之后的版本中,Scala 抛弃了自身的 Actor 而是使用了 Akka
作为其推荐的 Actor
实现。
隐式转换是通过在指定的上下文环境中定义一个类型转换的函数来在必要时完成变量的自动类型转换。和子类到父类的自动类型转换不同,隐式转换是通过函数来实现的,所以原类型和目标类型可以没有任何关系。
所谓的函数字面量指的将函数本身赋值给一个变量。通过函数字面量,可以使函数本身表现得和普通变量一样。
所谓的函数字面量指的将函数本身赋值给一个变量。在 Groovy 中,函数字面量是通过闭包来实现的。通过函数字面量,函数本身可以像普通变量一样进行各种操作。
完整的 Kotlin 方法定义语法为
完整的 Scala 方法定义语法为
完整的 Groovy 方法定义语法为
完整的 Java 定义语法为
this
通常用于指代当前对象,而在 Kotlin 中由于拥有闭包,扩展等特性,this
的指代变得更加复杂。类名.this
类指代外部类的对象,Kotlin 中可以使用 `this@类名
来达到同样效果。类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
apply
是 Scala 中的特殊方法。当代码中使用了 f(arg1,arg2...)
这样形式的语句,如果 f
不是函数或方法时,则 Scala 会自动调用 f.apply(arg1,arg2...)
。且当 f
为 class 时调用的是 object 的 apply()
方法,f
为 object 时调用的是 class 的 apply()
方法。
Extension 是 Kotlin 中一个非常强大的功能,主要用于对已经定义好的类的行为或者属性进行扩展,这一特性非常类似 Javascript 中的 Prototype。
多重声明类似 Scala 中的 unapply
,主要用于对对象进行拆箱。
实现多重声明只要在任意类内部定义了 componentN()
方法(N 为任意自然数)即可并加上 operator
关键字即可。
Lazy变量也就是常说的惰性加载,即变量在初始化时没有进行计算操作,而是延迟到了该变量第一次被使用的时候。在函数式编程中,惰性加载被广泛使用,尤其是对于一些长度未知的列表,如果使用普通方式,恐怕加载时系统就会被拖慢速度甚至于内存溢出。
注解主要用于标示元数据。Java 中声明一个注解使用符号 @interface
。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {
String name();
}
以上创建了一个名为 Bean
的注解。该注解上使用了标示此自定义注解的两个 JVM 的内置注解:Retention
和 Target
。
Java 中执行 Shell 命令主要依靠 Runtime 和 Process 两个类。
测试过程通常有 N 种分类,一般常说的有那么几种: UT,IT(CT),ST,MT。
UT 单元测试属于白盒测试,是测试中的最小单元,一般用于测试单独的方法,检测方法的正确性。
线程主要用于执行并发任务,提高 CPU 的运行效率。在各种参考中都有线程相关的各种概念,在这里就补多废话了。在Java 中线程相关的概念主要有两个: Thread 和 Runnable。
Groovy,Scala 和 Kotlin 都是 JVM 上的语言,在设计之初就考虑到了与 Java 的兼容性,所以这三门语言几乎都能无缝调用 Java 代码,因此也能很简单地实用现在众多成熟的 Java 类库。而 Java 调用这三门语言也不是太麻烦,所以可以根据实用场景在这四门语言中进行便捷地切换。
Java 中的 IO 操作主要是对各种流进行操作。Java 中流可以分为字节流(InputStream),字符流(FileReader),转换流(InputStreamReader)等。这种设计方式一直都被认为是非常优秀的。 由于流的种类繁多,使用起来有固定的套路,并不复杂,所以本章只是简单介绍一下。
基本概念同 Java。
基本概念同 Java。
Groovy 中使用的就是 Java 的泛型,所以参考 Java 就行了。但是要注意的是由于 Groovy 的动态特性,所以有些Java 会报的编译错误在 Groovy 中只有运行时才会发现。
泛型是 Java 1.5才引进的特性。泛型使类型参数化变得可能。在声明类或接口时,可以使用自定义的占位符来表示类型,在运行时由传入的具体类型进行替换。泛型的引入让集合变得更加好用,使很多错误在编译时就能被发现,也省去了一些强制转换的麻烦。
JSON 是一种键值对形式的轻量级的数据交换格式,除了大量用于 Restful 请求外,其二进制形式的 BSON 也被用于作为 NO SQL 的数据存储格式。相比较 XML 而言,JSON 更为简单,灵活。
XML 也可以用于数据交换,最为有名的就是 SOAP 协议。相比较 JSON 而言由于需要有开始和结束标记所以 XML 略显啰嗦。不过相比较 JSON 的无模式形式,XML 可以通过指定 DTD 而为其中定义的数据指定一定的格式,所以可以作为配置文件,在使用 IDE 进行编辑时也能拥有代码提示功能。
正则表达式功能非常强大,但是写好一个满足各种情况的合格的正则表达式却不是件简单的事。
本章节不会介绍任何正则表达式的语法,而是介绍四种语言中正则表达式最基本的三种用法:匹配,查找和替换的实现方式。
枚举是种特殊的类,主要用于构建一组特定的值。在 Java 中通常用于限定某个属性的值只能在一定范围内,在某些场合可以被静态常量代替,但是枚举可以有自己的属性也可以有自己的方法,而静态常量无法做到,且静态常量只能限定类型,不能限定值本身。当然在效率上,枚举并不如静态常量。
Trait 中文名为特质。特质是字段和行为的集合,可以拥有抽象成员也可以拥有普通成员。特质可以看做是一种特殊形式的接口,但是特质主要用于实现多重继承。
多重继承虽然便利,但是会带来 Diamond Problem,即 B 和 C 都实现了 A 的某个方法,而 D 继承了 B 和 C 但是没有重写该方法,此时调用 D 持有的该方法到底来自于 B 还是 C。因此过分使用特质会让程序本身难以理解。
package
来声明一个包。.
作为路径分隔符,且包路径必须与源文件路径相同。package a.b;
class B {}
Java 中异常是种特殊对象,可以分为检查异常和非检查异常,所有 RuntimeException 都属于非检查异常。
非检查异常表示程序正常运行时不应该会发生的异常,所以无需对非检查异常进行额外的检查。
data
声明的类toString()
, componentN()
, copy()
, equals()
和 hashCode()
方法,不在构造方法中定义的属性不会产生在 toString()
结果中。==
进行比较,同样不在构造方法中定义的属性不会用在比较上break
才会停止执行。abstract
声明的类。抽象类与普通类的最大区别是抽象类不能被实例化。abstract
声明。extends
继承普通类,继承时必须重写所有抽象方法。case class
声明的类。它可以隐式调用构造方法进行初始化,样本类没有副构造方法。toString()
, equals()
和 hashCode()
方法,不在构造方法中定义的属性不会产生在 toString()
的结果中。==
进行比较,同样不在构造方法中定义的属性不会用在比较上在 Java 中实现单例对象通常需要自己实现一个类并创建 getInstance()
的方法然后在该方法里使用两次同步块或者使用更为优雅的基于 enum
的方式。而 Scala 中则更加简单,只要使用 object
声明就能创建一个单例对象。实际上之前我们创建的拥有 main()
方法的都是单例对象。
所谓的内部类即定义在类内部的类,而包含这个内部类的类则被称作外部类。通常来说内部类可以访问外部类的私有成员,作为外部类的内部扩展而存在。
静态内部类即以 static
关键字声明的内部类。静态内部类不属于外部类的成员,使用上与普通的外部类没有什么区别。
Kotlin 同 Scala 一样使用关键字 class
来定义类,同时类的属性必须明确指明初始值。
class Person {
// 属性
var age = 0
// 行为
fun say(message: String) {
println(message)
}
}
Scala 中也使用关键字 class
来定义类,但是类的属性必须明确指明初始值,而不是像 Java 和 Groovy 一样有默认值。
class Person {
// 属性
var age = 0
// 行为
def say(message: String) {
println(message)
}
}
Groovy 中也使用关键字 class
来定义类
class Person {
// 属性
def age
// 行为
def say(message) {
println(message)
}
}
类在面向对象编程中是一个最基本的概念。类是对象的模板,用于产生具有相同结构的对象。一个类通常由属性和行为构成。
Java 中使用关键字 class
来定义类
映射是由键值对组成的一组数据的集合。映射通常基于哈希表,最常见的实现就是 HashMap。
元组类似列表,也是表示一组数据,但是元组中的数据通常都是不同类型或者数据间表示不同含义。且元组通常只用于传递数据,所以不能进行修改。
数组在程序中用于表示一组特定的值,通常来说数组的大小是预先指定的,数组的元素类型也是统一的,所以访问数组时可以通过偏移量快速访问任意一个元素。
集合类似数组,很多时候集合也是通过数组实现的,但是集合的长度是可变的,存储的数据类型也可以不一样(尽管一般都是存储同类型的数据)。集合通常有两种:List 和 Set,一般来说,前者有序且元素可以重复,后者无序但元素不能重复。
for
语句可以说是最常用的循环语句了。Java 支持普通的 for
语句以及增强型的 for-each
语句。
for 语句
for (int i = 0; i < 3; i++) {
System.out.println("Repeat For " + i);
}
if 语句用于表达这样一种概念,在某种条件下执行一种操作,在另一种条件下执行另一种操作。 Java 中的 if 语句如果操作只有一行语句的话可以省略花括号,尽管很多书籍不建议使用这种方式。
脚本,是使用一种特定的描述性语言,依据一定的格式编写的可执行文件。通常可以用于执行一些简单的代码。
Java 使用关键字 null
表示空值,即没有任何引用。使用 ==
来判断一个值是否是空值。
Integer x = null;
System.out.println(x == null);
四种语言都支持以下三种注释
// 单行注释
/*
多行注释
*/
/**
* JavaDoc
*/
String s = "Hello World";
for (char c : s.toCharArray()) {
System.out.println(c);
}
语法
数据类型 变量名 = 变量值;
例
String variable = "foo";
Java 的数据类型主要分为基本数据类型和引用数据类型。
Java 的基本类型有 int, long, float, double, char, boolean,即以小写字母开头的数据类型。
本章主要介绍四种程序的基本程序结构和特性。
REPL 为 “Read Evaluate Print Loop” 的简写,即 “读取-求值-打印-循环”,是一些动态语言的标准特性,主要用于进行探索式编程。也就是说当你不清楚某项计算的结果或者忘记了某个方法的作用时,可以输入代码片段并立即获得结果。
在进行各种测试之前,我们先为之后的所有例子建立一个完整的工程项目。我使用的开发工具为 IDEA 14 CE,构建工具选择最为通用的 Maven。
本系列主要是对 Java,Groovy,Scala 和 Kotlin 这四种 JVM 上的语言做一些粗浅的对比。对于那些语言本身提供但是却不提倡使用的功能本系列大都不会提。本系列主要使用的开发环境为 Windows 和 Mac,IDE 为 IDEA 和 Eclipse,构建工具为 Maven 和 Gradle。