文档章节

“Effective Java” 对 Kotlin 设计的影响 — Part 1

编辑部的故事
 编辑部的故事
发布于 2017/01/18 21:21
字数 1736
阅读 5.2K
收藏 55

Java 是一门非常不错的编程语言,但也存在一些缺陷,部分缺陷从 1995 年的早期版本延续至今。在 Joshua Bloch 出版的 Effective Java 一书中,作者详细介绍了避免常见编码错误及处理的方式。它包含 78 项,从语言的不同方面给读者提供了宝贵的意见。

现代编程语言创造很占优势,因为创造者可以从已创建的语言中学习和借鉴,然后开发出更好的编程语言。Jetbrains,一家捷克的软件开发公司,已创建了多个知名的 IDE,并于 2010 年创建了编程语言 Kotlin。该语言消除了 Java 中存在的一些问题,更简洁也更易表达。因之前的 IDE 全部用 Java 编写,现在他们急需一种与 Java 高度互操作的语言,并编译为 Java 字节码。Jetbrains 希望 Java 开发者能快速适应 Kotlin,同时用 Kotlin 构建一个更好的 Java。

重读 Effective Java 时,我发现许多建议对 Kotlin 来说都不那么必要了,所以,在这篇文章中,我想总结性地介绍一下这本书是如何影响 Kotlin 的设计的。

1、Kotlin 的默认值不需要更多构建器

当 Java 的 constructor 中存在多个可选参数时,代码会变得冗长,难读且容易出错。为解决这一问题,Effective Java 在第 2 项中介绍了如何有效的使用构建器模式。此类对象的构建需要用到许多代码,如下面代码示例中的 Nutrition 成分对象,它包含两个必须的参数(servingSize, servings)和四个可选参数(calories, fat, sodium, carbohydrates):

public class JavaNutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }

        public JavaNutritionFacts build() {
            return new JavaNutritionFacts(this);
        }
    }

    private JavaNutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

用 Java 实例化对象,如下所示:

final JavaNutritionFacts cocaCola = new JavaNutritionFacts.Builder(240,8)
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();

使用 Kotlin,则不需要使用构建器模式,因为有默认参数的功能,你可以为每个可选构建函数参数定义默认值:

class KotlinNutritionFacts(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

在 Kotlin 中创建一个对象,如:    

val cocaCola = KotlinNutritionFacts(240,8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

为了实现更佳的可读性,你还可命名所需的参数 servingSize 和 servings:

val cocaCola = KotlinNutritionFacts(
                servingSize = 240,
                servings = 8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

和 Java 一样,此处创建的对象不可变。

我们将代码行数从 Java 中的 47 减少到了 Kotlin 的 7,从而有效提高了生产力。

提示:如果要在 Java 中创建 KotlinNutrition 对象,我们可以这样做,但是必须为每个可选参数指定一个值。幸运的是,如果添加 JvmOverloads 注解,则会生成多个构造函数。 注意,如果要使用注解,则需要关键字 constructor:

class KotlinNutritionFacts @JvmOverloads constructor(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

2、轻松创建单例模式

Effective Java 的第 3 项介绍如何将 Java 对象设计为单例,它实际上是一个对象,其中只有一个示例可以实例化。下列示例进行了演示,其中只有一个 Elvis 存在:

public class JavaElvis {

    private static JavaElvis instance;

    private JavaElvis() {}

    public static JavaElvis getInstance() {
        if (instance == null) {
            instance = new JavaElvis();
        }
        return instance;
    }

    public void leaveTheBuilding() {
    }
}

Kotlin 有对象声明的概念,它给我们提供了一个单例的行为:

object KotlinElvis {

    fun leaveTheBuilding() {}
}

完全不用手动构建!:)

3、equals() 和 hashCode() 开箱即用

函数式编程和简化代码的良好实践,主要是为了使用不可变值对象。第 15 项中给出了建议:“除非有可变的合适理由,否则类不可变。”Java 中的不可变值对象非常繁琐,因为对于每个对象,你都必须重写 equals() 和 hashCode() 函数。Joshua Bloch 花了 18 页来描述如何遵守第 8 项和第 9 项提到的约束。例如,如果你重写 equals(),你必须保证反身性,对称性,传递性,一致性和非零性的约束都得到满足。这听起来更像是数学而不是编程。

在 Kotlin 中,你可以简单地使用数据类,编译器会自动派生 equals() 和 hashCode() 等方法。这是可以实现的,因为标准功能可以机械地从对象属性导出,你只需在类前输入关键字 data 即可。

提示:最近,Java 的 AutoValue 开始流行起来,这个库为 Java 1.6+ 生成不可变的值类。

4、属性而非字段

public class JavaPerson {

    // don't use public fields in public classes!
    public String name;
    public Integer age;
}

第 14 项建议在公共类中使用访问器方法而不是公共字段。如果不这么做的话,可能会引来一堆麻烦,因为字段之后可直接访问,这样以来你就无法享受封装和灵活性带来的好处。这也意味着,如果不更改类的公共 API,你将无法更改其内部表示。例如,你无法限制字段的值,如员工的年龄等。这也是我们在 Java 中创建默认 getter 和 setter 的原因之一。

这个最佳实践由 Kotlin 强制执行,因为它有自动生成默认 getter 和 setter 的属性而不是字段。

class KotlinPerson {

    var name: String? = null
    var age: Int? = null
}

在语法上,您可以使用 person.nameor person.age 访问 Java 中的公共字段等属性,稍后再添加自定义 getter 和 setter,而无需更改类的 API:

class KotlinPerson {

    var name: String? = null

    var age: Int? = null
    set(value) {
        if (value in 0..120){
            field = value
        } else{
            throw IllegalArgumentException()
        }
    }
}

小结:使用 Kotlin 的属性,我们可以得到更简洁的类,具有更大的灵活性。

5、重载必须关键字而不是可选注释

Java 1.5 中添加了注释,其中最重要的一个是 Override,它标志着一个方法重载了超类的一个方法。第 36 项介绍说,这个注释用以避免恶性 Bug。当你认为你在重写超类中的一个方法,但实际上并不是的时候,编译器将抛出一个错误。只要你别忘记写 Override 注解,它就能起作用。

在 Kotlin 中,override 不是可选注解,而是必须关键字,所以 Bug 出现机会不多。 

(未完待续)

原文:How “Effective Java” may have influenced the design of Kotlin — Part 1

责任编辑:开源中国达尔文

© 著作权归作者所有

编辑部的故事

编辑部的故事

粉丝 1552
博文 266
码字总数 522181
作品 0
深圳
运营/编辑
私信 提问
加载中

评论(14)

ChicStudio
ChicStudio
YANGL
YANGL
java++
scala--
ytk123
ytk123
个地方官的个地方官
__loong
__loong
真的很不错的语言~已经用在生产环境了。。
m
melin1204

引用来自“limchuenyu”的评论

kotlin对比groovy如何 ?
kotlin是静态语言,groovy是动态语言,当着脚本使用不错,用在生产gc很难控制。
V
Voyager_1
kotlin对比groovy如何 ?
GuoYJ
GuoYJ
吃了一个惊
xiaolei123
xiaolei123
kotlin称之为JVM界的Swift
m
melin1204
屏蔽了java的缺点,100%兼容java,又具有函数式语言的特性,能够看到scala的影子,但没有scala那么复杂,scala包含作者太过理想性的东西在里面,太过研究性,学习成本很大。对于不想学习scala的人,可以学习kotlin。
yybear
yybear

引用来自“Qiujuer”的评论

Kotlin 已经与Java理念不相关,仅仅只是说可以编译为Java class而已。

完全可以当做新语言来学习。
同意
从 Java 到 Scala,再到 Kotlin

在 Java 之后,JVM 平台上出现了一些其他的编程语言,Scala 和 Kotlin 可以算是其中的佼佼者。Scala 已成为大数据领域的明星,Kotlin 在 2017 年 Google IO 大会之后,俨然也成为了安卓平台潜...

ScalaCool
2019/05/31
0
0
热闹背后的冷静思考:Kotlin 和 Checked Exception

本文转载自当然我在扯淡,原文《Kotlin 和 Checked Exception》,作者:王垠。文章转载已获授权。 最近 JetBrains 的 Kotlin 语言忽然成了热门话题。国内小编们传言说,Kotlin 取代了 Java,...

局长
2017/05/24
5.1K
90
《Kotlin 程序设计》第九章 Kotlin与Java混合调用

第九章 Kotlin与Java混合调用 正式上架:《Kotlin极简教程》Official on shelves: Kotlin Programming minimalist tutorial 京东JD:https://item.jd.com/12181725.html 天猫Tmall:https:/......

程序员诗人
2017/05/29
0
0
谷歌大牛说:为什么 Kotlin 比你们用的那些垃圾语言都好

谷歌技术大牛 Steve Yegge 也写了一篇关于 Kotlin 的使用体会总结文章,同样也在首页热门,并引发 200+ 讨论。 (Steve Yegge ) 为什么说 Kotlin 比你们用的那些垃圾语言都好 说真的,其实我...

两味真火
2017/05/31
1.4W
88
资深开发者带你看看 JVM 语言的浮沉史

不时地,总会有预言 Java 将要死掉的文章冒出来,有意思的地方在于他们都没有写上一个日期。不过说实话,他们说的可能都是正确的。这是每一门语言的命运:消失或被遗忘 — 或者更准确的说是越...

局长
2017/08/24
3.8K
30

没有更多内容

加载失败,请刷新页面

加载更多

MBTI助你成功,让你更了解你自己

MBTI助你成功,让你更了解你自己 生活总是一个七日接着又一个七日,相信看过第七日的小伙伴,很熟悉这段开场白,人生是一个测试接着又一个测试,上学的时候测试,是为了证明你的智力,可谓从...

蛤蟆丸子
今天
55
0
Android实现App版本自动更新

现在很多的App中都会有一个检查版本的功能。例如斗鱼TV App的设置界面下: 当我们点击检查更新的时候,就会向服务器发起版本检测的请求。一般的处理方式是:服务器返回的App版本与当前手机安...

shzwork
昨天
72
0
npm 发布webpack插件 webpack-html-cdn-plugin

初始化一个项目 npm init 切换到npm源 淘宝 npm config set registry https://registry.npm.taobao.org npm npm config set registry http://registry.npmjs.org 登录 npm login 登录状态......

阿豪boy
昨天
87
0
java基础(16)递归

一.说明 递归:方法内调用自己 public static void run1(){ //递归 run1(); } 二.入门: 三.执行流程: 四.无限循环:经常用 无限递归不要轻易使用,无限递归的终点是:栈内存溢出错误 五.递...

煌sir
昨天
63
0
REST接口设计规范总结

URI格式规范 URI中尽量使用连字符”-“代替下划线”_”的使用 URI中统一使用小写字母 URI中不要包含文件(脚本)的扩展名 URI命名规范 文档(Document)类型的资源用名词(短语)单数命名 集合(Co...

Treize
昨天
69
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部