设为首页 加入收藏

TOP

[外文翻译]《Effective Java》在哪些方面影响了 Kotlin ?的设计?(一)
2017-06-26 10:23:25 】 浏览:441
Tags:外文 翻译 Effective Java 哪些 方面 影响 Kotlin 设计

Java是伟大的编程语言无疑,但它也有一些众所周知的缺陷,比如那些常见的坑和从早期继承下来的不太重要的东西(Java 1.0发布于1995年)。 Joshua Bloch写了一本颇受推崇的书叫《Effective Java》,内容是关于如何写出好的Java代码,同时避免常见的编码错误及如何应对Java的不足。它有78个章节,称为“条目”,从多个方面为读者提供关于Java编程的宝贵建议。


现代编程语言的创造者有很大的优势,因为他们能够分析现有语言的缺点,并在设计语言的时候尽量避免。Jetbrains是一家开发了几款非常受欢迎的IDE的公司,于2010年决定为自己的开发工作创造一种编程语言——Kotlin。它的目标是更简洁、更有表现力,同时避免Java的一些不足。这家公司之前发布的所有IDE都是用Java编写的,所以他们需要一种与Java高度互操作的语言,并能够编译成Java字节码。他们还希望Java开发人员可以轻松切换到Kotlin. 也就是说,Jetbrains希望构建一个更好的Java。


在重读《Effective Java》时,我发现其中的很多内容对Kotlin来说已经用不着了,所以产生了一个想法,想探讨一下这本书是否影响了Kotlin的设计。


1. Kotlin 的默认值不再需要builder


当Java构造函数有很多可选参数时,代码将变得冗长,可读性差且容易出错。针对这个问题,Effective Java的条目2讲述了如何有效地使用构造器模式(Builder Pattern)。构建这样的对象需要写很多代码,如下面的代码示例中的“营养学”对象。它有两个必需的参数(serveSize,servings)和四个可选参数(calories, fat, sodium, carbohydrates):


要用Java实例化一个对象,就得这样:


而在Kotlin中,你不再需要使用构造器模式,因为它有默认参数的功能,允许你为每个可选的构造函数参数定义默认值:


所以在Kotlin中创建对象就可以这样:


如果想让可读性更强,你也可以把必需的参数命名为 servingSize 和 servings


跟Java一样,这里创建的对象是不可变的。


我们将Java的47行代码减少到了Kotlin的7行,大大提高了生产力。


图0:[外文翻译]《Effective Java》在哪些方面影响了 Kotlin ?的设计?


温馨提示:如果想用Java创建这样的 KotlinNutrition 对象当然也是可以做到的,但你得为每个可选参数设定一个值。还好,只要加上 JvmOverloads 注解,那么就会自动生成多个构造器,使用注解时需要 constructor关键字:


2. 创建单例(singleton)很容易


Effective Java 的条目3说了如何设计一个单例Java对象,也就是只能实例化一个实例的对象。下面的代码片段展示了一个“单向”的宇宙,其中只能存在一个猫王:


Kotlin 有“对象声明”的概念,可以方便的通过对象声明来获得一个单例。


再也不用费劲构造单例了。


3. 开箱即用的 equals() 和 hashCode()


良好的编程实践起源于功能编程,简化代码主要靠使用不可变值对象。条目15建议“类应该是不可变的,除非有足够的理由将它们设为可变”。创建不可变的值对象在Java中非常繁琐,因为你必须为每个对象重写equals()和hashCode()。Joshua Bloch在条目8和9用了足足18页描述了关于这两种方法的准则。例如,如果你重写equals(),你必须保证自反性、对称性、传递性、一致性和无效性,听起来不像在编程而更像数学。


在Kotlin中,这种情况下你可以直接使用数据类,编译器会自动导出equals(),hashCode()等方法。这是因为标准方法可以从对象的属性中直接派生出来,只需在类前面输入关键字数据即可,完全不需要18页的描述。


提示:最近,Java的AutoValue很流行,该库可为Java 1.6+生成不可变值类。


4. 属性(properties)取代域(fields)


条目14建议在公有类中使用访问器方法而不是公有字段。如果您不这么做的话可能会遇到麻烦,因为域可以直接访问,导致完全享受不到封装好处。这意味着日后你将无法在不改动其公共API的情况下更改该类的内部表达。比如,后面你就不能再去限制某个字段的值,例如人的年龄。这就是为什么我们总是在Java中创建这些冗长的默认getter和setter的原因之一。


而Kotlin直接用自动生成默认getter和setter的属性取代了字段/域。


从语法上来说,你可以使用person.name 或者 person.age访问Java中的公共字段等属性。之后也可以添加自定义的getter和setter而无需更改类的API:


长话短说:有了Kotlin的属性,我们的类将更简洁,同时还具有与生俱来的灵活性。


5. override成为强制关键字而不是可选注解


Java 1.5 中加入了注解(annotation),其中最重要的一个是重写(override),表示这个方法是对超类中该方法的重写。基于书中条目36,应该尽量使用这个可选注解以避免一些恶心的bug。比如当你以为你重写了超类的方法但其实并没有时,编译器会抛出一个错误。不过如果你记得加上了override注解的话就没事。


在Kotlin中,override不是可选的注解而是强制关键字。所以由此引发的bug就不会再有了,编译器会提前警告你。


6. 默认的 final 类


《Effective Java》在第17条说,要么为继承而设计,并提供文档说明,要么就禁止继承。在Java中,除非将类显式指定为final,否则每个类都可以被继承。如果你忘记把类指定为final,也没有好好为继承而设计,那么当客户创建子类并覆盖某些方法时,很可能功能会出问题。


在Kotlin,所有类默认都是final的。如果要允许继承,则必须明确使用关键字open,这与Java的final完全相反。这样可以避免创建并非有意设计继承的非final类。


Kotlin社区有人对这个 “默认的final” 设计很不满。Kotlin论坛对此进行了激烈的讨论。后来,在Kotlin 1.1 beta版中提供了一个编译器插件,可以让class默认是open.


7. 没有检查型异常(checked exceptions)


Java有一个广为人知的特性,即检查型异常,编译器会强制函数的调用者捕获(或重新抛出)异常,这个功能总是容易出问题。 《Effective Java》花了一整个章节来阐述如何正确的使用和处理检查型和非检查型(即运行时)异常。


如Kotlin的文档中所述,检查型异常的一个问题是你有时必须捕获永远不会发生的异常,这将导致空的catch块和冗余代码。而且开发人员经常在被迫处理异常感到麻烦而直接忽略它们,这也会导致空的catch块。第65项说“不要忽视异常”。


根据第59条,检查型异常往往是不必要的,而且这应该通过在调用对象之前检查对象的状态,或者通过判断返回值(比如null)来避免。


我还发现了检查型异常的其它问题:


由于存在大量潜在问题,Kotlin等优秀编程语言(如C#)没有检查型异常。为了让调用者知道可能发生的异常,应该用 throws 标签在函数的文档中定义它们。


8. 强制的 null 检查


在Java中,public方法的方法签名不会告诉你返回值是否为空。例如:


如果一

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Spring3.0使用annotation完全代替.. 下一篇Python基础之字典dict和集合set

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目