本文共 5546 字,大约阅读时间需要 18 分钟。
Kotlin 作为后起之秀,站在巨人们的肩膀上是她得天独厚的优势,而这个巨人也包括《Effective Java》(EJ),得益于这个巨人,Kotlin 到处散发着高效的味道,这篇文章让我们一起来领略下 Kotlin 的高效之道。
在实例化对象的方式中,使用静态工厂方法相比构造器有几个好处:工厂方法拥有名字,易于开发者理解。不必在每次调用的时候都创建一个新对象,比如可以事先缓存好实例。可以返回原类型的任何子类型。Kotlin 并没有 static 关键字,也没有静态成员的概念,取而代之的是『伴生对象』,因此,对于第一条准则,Kotlin 使用伴生对象关键字 companion 来定义静态工厂方法,代码风格如下:
class User private constructor(val account:String){ companion object { fun newWeiboUser(email:String):User{ return User(email) } fun newTelUser(tel:Long):User{ return User(tel.toString()) } }} 调用方式类似 Java 中的静态方法:
val newTelUser = User.newTelUser(18888888888)val weiBoUser = User.newWeiboUser("geniusmart") 对于单例模式,Kotlin 对单例模式做了更彻底的精简,简直易如反掌,可以通过 object 关键字声明一个单例类的同时创建一个实例,例如:
object singleton { fun action() { println(this.hashCode()) }} 验证单例的唯一性:
@Testfun test() { val instance1 = singleton val instance2 = singleton assertEquals(instance1, instance2)} 在 Java 中,单例模式通常使用私有构造器和静态初始化块:
public final class singleton { public static final singleton INSTANCE; private singleton() { INSTANCE = (singleton) this; } static { new singleton(); }} Kotlin 让创建单例变得更高效。
封装(信息隐藏)是面向对象的四大特性之一,体现在具体的实现层面便是四种访问权限:private、default、protected 和 public。面向对象编程,我们的代码充满着类、成员属性和成员方法,这些都是我们对外的契约。如果类和成员都是可访问的,意味着我们后续的迭代版本都必须保持兼容,这显然是一项巨大的工程。反之,充分利用好四种访问权限,将类和成员的可访问性控制到最小,更有利于程序的扩展。
Kotlin 的默认访问权限为 public,没有包级别访问权限,新增了模块可见的访问权限 internal,并支持顶层声明的类别。关于 internal,举个栗子:假设工程里有两个 module,app 和 lib,app 依赖于 lib 工程,代码层级如下:
app-- class Activitylib-- internal class StringUtils
StringUtils 仅在 lib 工程中可视,app 工程中的 Activity 无法访问该类。Kotlin 在访问权限的设计更彻底地贯彻了“使可访问性最小化”的准则。
《Effective Java》中提到,使用访问方法而非公有域。例如:
public class Point { public double x; // 公有域 public double y; } 这样的代码直接暴露成员属性,且未来难于更改实现。Kotlin 在语法层面直接约束了这一点,默认将属性定义为私有,并提供 public 修饰的 get 和 set 方法:
class User { val num = 10 // 属性默认为 private,且拥有 public 的 getNum() var nickname = "geniusmart" // 同上}@Testfun test() { val user = User() println(user.num) // 实际上调用的是 getNum() user.nickname = "Mr.Geniusmart" // 实际上调用的是 setNum() println(user.nickname)} 如果需要对属性进行更复杂的处理,可以通过 get 和 set 方法进行扩展:
class User { val num = 10 var nickname = "geniusmart" get() = field.plus("@email.com")} Kotlin 的 set 和 get 方法完美吻合《Effective Java》中第14条准则。
《Effective Java》中提到,组合优先于继承(原书是复合优先于继承)。继承破坏了封装性,父类必须暴露更多的细节让子类知道(比如使用 protected 访问权限),同时子类依赖于父类的实现,一旦父类改变,子类都会受影响。
例如,想对 HashSet 增加『计算新增元素个数』的能力,采用继承的方式:
class CountingSet: HashSet{ var count = 0 override fun add(element: String): Boolean { count++ return super.add(element) } override fun addAll(elements: Collection ): Boolean { count += elements.size return super.addAll(elements) }}
验证发现,父类的 addAll() 会循环调用 add(),导致计数器倍增。相反,使用组合的方式:
class CountingSetBy(val countingSet: MutableCollection): MutableCollection by countingSet { var count = 0 override fun add(element: String): Boolean { count++ return super.add(element) } override fun addAll(elements: Collection ): Boolean { count += elements.size return super.addAll(elements) }}
验证发现,组合的方式实现更高效且可靠。
《Effective Java》中提到,要么为继承而设计,并提供文档说明,要么就禁止继承。Kotlin 中创建的类和方法默认都是 final:
class Parent { fun action() {}} 如果经过深思熟虑,一定要提供继承和重写,可以通过 open 修饰符来实现。
《Effective Java》中提到,优先考虑静态成员类。Kotlin 在语法层面直接支持嵌套类,默认为静态成员类:
class Outer { class Inner { }} 如果需要非静态成员类,可以通过 inner 关键字实现:
class Outer { inner class OtherInner { fun action() { // 调用外部类实例 this@Outer.toString() } }} 《Effective Java》中提到,使用静态成员类而非非静态成员类,Kotlin 的嵌套类设计符合这一原则。
《Effective Java》中提到,重写方法必须使用 override 注解。Kotlin 也严格执行这一规则,强制在重写方法时添加 override。
Kotlin 的 DSL(领域特定语言)让代码更具表现力和想象力。以下是 Kotlin DSL 的一些有趣实现:
val yesterday = 1.days.agoval twoMonthsLater = 2.months.fromNow
val str = "kotlin"str should startWith("kot")str.length shouldBe 6 fun createTable() = table { tr { td {} } }println(createTable()) (Users innerJoin Cities) .slice(Users.name, Cities.name) .select { (Users.id.eq("andrey") or Users.name.eq("Sergey")) and Users.id.eq("sergey") and Users.cityId.eq(Cities.id) } .forEach { println("${it[Users.name]} lives in ${it[Cities.name]}") } class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } }} dependencies { compile("com.android.support:appcompat-v7:27.0.1") compile("com.android.support.constraint:constraint-layout:1.0.2")} Kotlin DSL 的代码结构具有以下特点:链式调用、大括号嵌套,并且可以近似于英语句子。
Kotlin 的实现原理包括扩展函数、lambda、中缀调用和 invoke 约定。这些特性使得 Kotlin 的代码既整洁又高效。
扩展函数(扩展属性)让 Kotlin 拥有类似动态语言的灵活性:
fun Int.days(): Period = Period.ofDays(this)val Int.days: Period get() = Period.ofDays(this)
lambda 是构建整洁代码的利器:
fun printSum(sum: (Int, Int) -> Int) { val sum = { x: Int, y: Int -> x + y } printSum(sum)} 中缀调用让代码更接近英语:
"key" to "value"
invoke 约定让对象可以像函数一样调用:
class Person(val name: String) { operator fun invoke() { println("my name is $name") }}val person = Person("geniusmart")person() 总结起来,Kotlin 的 DSL 代码结构具有链式调用、大括号嵌套、接近英语句子的特点。这些特性使得 Kotlin 的代码既整洁又高效。
原文:[《Effective Java》与 Kotlin 的结合](https://mp.weixin.qq.com/s/1h8FqY Rosenberger)
转载地址:http://gjma.baihongyu.com/