毫无疑问,当谈到编程言语时,Java 已占据上风,并被以为是最主要的开辟言语之一。但是,在基于 JVM 的根底上还开辟了一些言语,比方说 Kotlin 。
Kotlin 关于当代化的多平台使用是一种静态类的编程言语。虽然我曾经从事 Java 开辟有相当长的工夫,可是在做一个数据-匿名化的项目时使我觉得到 Java 需求从 Kotlin 引入一些工具。
下面是一些我情愿看到的在 Java 中占领一席之地的 Kotlin 特征。
其它翻译版本 (2) 加载中Java 9 引入了工场办法创立聚集,以此倡导不成变。但能在言语级别集成不成变,而非经过包装生成不成变聚集将长短常不错的方法。existingDepartments() 是一个 Kotlin 的前往一个不成变字符串列表的函数。
//Kotlinfun existingDepartments(): List<String> = listOf("Human Resources", "Learning & Development", "Research")
Java 9 在测验考试添加、删除一个不成变列表中的元素时将会抛出一个 UnsupportedOperationException 异常。但如可变与不成变的接口完整隔离,防止给任何不成变聚集表露任何添加、删除的接口将是不错的。
//pre Java 8 public List<String> existingDepartments() { return new ArrayList<String>(){{ add("Human Resources"); add("Learning & Development"); add("Research"); }}; }//Java 8public List<String> existingDepartments() { return Stream.of("Human Resources", "Learning & Development", "Research") .collect(Collectors.toList()); }//Java 9public List<String> existingDepartments() { return List.of("Human Resources", "Learning & Development", "Research"); }
明晰看待不成变聚集,果断支持表露接口然后抛出 UnsupportedOperationExceptions 异常。
为晋升不成变性及防止因为变更招致的过错,最少可以思索使缺省的办法参数为 final 。
//Kotlinfun add (augend: Int, addend: Int) = augend + addend
add() 函数的参数是 val ,其默许是不克不及改动的,这意味着作为任何函数的客户端,我都可以担心:函数不会改动通报给它的参数(不要与 object mutation 混杂)。
使办法参数默许为 final 可能而且很大可能会毁坏 Java 晋级后的现存代码根底,但值得一提。
一切的java开辟者必定晓得臭名远扬的NullPointerException。Kotlin迈出了主要的一步在编译时处置NULLs。在显式声明之前,一切都长短空的。
Java 8不是出于异样的缘由引入了Optional吗?让我们看一个例子:
//Kotlinclass Employee(private val id: Int, private val department: Department?) { fun departmentName() = department?.name ?: "Unassigned"} class Department(val name: String)/** Employee needs a non-nullable "id" and an optional department to be constructed. val employee = Employee(null, null) => <b> Compile Time Error </b> **/
这个Employee 类具有非空id和可选(可空)部门的主结构函数。为id通报null将招致编译时过错。
departmentName()函数运用可选操纵符拜访Department的name属性?在可空的字段上,假如department为null,name将会不被拜访而且左手边的表达式[department?.name]将会前往null。三元操纵符?:假如在表达式的右边为null将会前往右手边的 ("Unassigned")。
//Java 8class Employee { private Integer id; private Optional<Department> department Employee(Integer id, Optional<Department> department){ this.id = id; this.department = department; } public String departmentName() { return department.orElse("Unassigned"); } }/** Employee needs a non-nullable "id" and an optional department to be constructed. Employee employee = new Employee(null, null); <b>NPE !!!</b> **/
Optional不会维护NPE的代码,可是也有它的长处:
它使域模子变得明晰。Employee类有可选的department,这就足以得出结论,每一个员工都可能没有被分派到一个部门。
它在departmentName()办法中增进可组合性。
在编译时处置NULLs应当可以经过删除if语句、工具的方式中的不用要的NULL反省来完成更洁净的代码。Objects.requireNonNull, Preconditions.checkNotNull等其他任何方式(的不用要的NULL反省)。
为了坚持容易,department被通报给结构函数,虽然这是一个可选属性。
改良Lambda表达式
Java 8引入的lambda表达式是树立在函数式接口和函数描绘符的根底上的,这就意味着每个lambda表达式城市映照到一个界说在函数式接口中的笼统办法。这是一种高效的办法,他受权给一个只要一个笼统办法(即函数描绘符)的接口(即函数式接口)可以创立一个lambda表达式。
//Kotlinval isPositive: (Int) -> Boolean = { it > 0 } OR, val isPositive: (Int) -> Boolean = { num > 0 } OR, val isPositive: (Int) -> Boolean = { num: Int > 0 }//UsageisPositive(10) returns trueisPositive(-1) returns false
上述代码中,变量isPositive就是一个函数,它承受一个整形变量为参数而且前往一个布尔类型值。这个变量的值就是一个函数的界说或许是界说在花括号中的一个lambda表达式,这个函数可以反省传出去的参数能否大于零。
但是,鄙人面的代码中,Predicate是包括一个笼统函数test()的函数式接口——他可以承受一个类型为T的参数并前往一个布尔类型值。
因而,isPositive可以承受一个整型变量作为参数并反省这个参数能否大于零。所以我们需求在运用isPositive时挪用test()办法。
//Java 8 private Predicate<Integer> isPositive = (Integer arg) -> arg > 0; //Usage isPositive.test(10) returns true isPositive.test(-1) returns false @FunctionalInterface public interface Predicate<T> { boolean test(T t); }
lambda表达式不该该依靠于函数式接口和此中的函数描绘符。
Kotlin 支撑扩大函数,以在无需承继或装潢其他类的状况下给函数供给新功用。
以下是一个扩大函数的案例,前往 String 最初的一个字符。
//Kotlinfun String.lastChar() = this.toCharArray()[this.length - 1]/** Extension functions are of the form - fun <ReceiverObject>.function_name() = body OR, fun <ReceiverObject>.function_name(arg1: Type1, ... argN: TypeN) = body **/
lastChar() 是界说在 String 类上的一个拓展函数,这里 String 也被称为接纳工具。经过 "Kotlin".lastChar() 来触发函数。
拓展函数在没有承继和任何设计形式的状况下,可以拓展函数新特征。
Kotlin 支撑 Tail-recursion(尾部递归)。尾部递归是递归的一种方式,其递归挪用的是函数尾部最初的一条指令。如许一来,我们不必担忧之前的值,一个栈桢知足一切的递归挪用;尾部递归是优化递归算法的一种方法。
别的,尾部递归可以很轻易的转换为迭代的方法。
//Kotlinfun factorialTco(val: Int): Int { tailrec fun factorial(n: Int, acc: Int): Int = if ( n == 1 ) acc else factorial(n - 1, acc * n) return factorial(val, acc = 1) }
当函数用 tailrec 润饰符标识并知足所需的方法时,编译器会对递归实行优化,并以疾速高效的递归版本交换。
实践而言,尾部递归以牢固的栈桢空间履行,它只是迭代进程的另外一种表达式。
Java 其实不在编译器级别间接支撑尾部挪用优化,不外可以经过 lambda 表达式完成,可是等待能在编译层面看到尾部递归算法。
移除固有的复制 [new,return,semicolon]:Kotlin 不需求 new 来创立一个实例。假如函数被视为语句而不是表达式,它依然需求 return 。
//Kotlinclass Employee(private val id: Int, private val department: Department?) { //no return fun departmentNameWithoutReturn() = department?.name ?: "Unassigned" //return is needed if a function is treated as a statmentrather than an expression fun departmentNameWithoutReturn() { val departmentName = department?.name ?: "Unassigned" return departmentName } }
单例类:在 Java 中创立单例类假如有更容易的办法将会很棒。Kotlin 中的等效语法以下所示。
//Kotlinobject DataProviderManager { fun registerDataProvider(provider: DataProvider) { // ... } }
不成变类: 假如能看到相似 readonly/immutable 润饰符来创立一个不成变类那真是极好的。下面提到的代码片断仅是一个设法(在 Kotlin 或 Java 中不成用)。
//Hypothetical [Not available so far]immutable class User(private val name: String, private val id: Int)
总之,作为开辟职员,我们总会出错误(遗漏 NULL 反省、修改聚集数据等),但在言语级别上供给这些功用将使编程更轻松并尽量防止呈现过错。
其它翻译版本 (1) 加载中 本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协定,假如我们的任务有进犯到您的权益,请实时联络我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务