【CSDN编者按】现在Kotlin语言越来越流行。它不仅广泛用在移动应用开发上,也能用于服务器端系统上。你也许知道,Kotlin是个运行在JVM上的静态类型编程语言。

Kotlin之所以流行的主要原因之一就是简单。它删掉了许多Java中华而不实的代码。但是,它与Java也很相似,因此任何有经验的Java程序员都能在几个小时之内学会使用Kotlin。

这篇文章中,作者将讨论在服务器端使用Kotlin时的几个有趣的功能,并与Java作比较。下面是作者个人喜欢的Kotlin有而Java却没有的功能。

 集合和泛型

我很喜欢Java,但有时候泛型集合非常不好用,特别是在需要使用通配符类型的时候。好消息时,Kotlin没有任何通配符类型,而是提供了另外两种功能,分别称为“声明端型变”和“类型投射”。我们来考虑下面的类层次结构。

abstract class Vehicle {}class Truck extends Vehicle {}class PassengerCar extends Vehicle {}

我定义了一个泛型的仓库类Repository来容纳给定类型的所有对象。

public class Repository<T> {  List<T> l = new ArrayList();  public void addAll(List<T> l) {    this.l.addAll(l);  }  public void add(T t) {    l.add(t);  }}

现在,我想把所有的车辆都保存在这个仓库中,于是我定义Repository r = new Repository()。但是,用List作为参数调用仓库类的方法addAll会产生错误:

即使更改Repository中的addAll的定义,也会收到下面的错误:

当然,这种情况可以得到合理的解释。首先,Java中的泛型是不可变的,也就意味着List<Truck>不是List<Vehicle>的子类型,尽管Truck是Vehicle的子类型。

addAll方法接受通配符类型参数,并扩展T作为类型参数,这意味着该方法接受一个由类型为T或T的子类型的对象的集合作为参数,而不仅仅是类型为T的对象的集合。

List<Truck>是List<? extends Vehicle>的子类型,但目标列表依然是List<Vehicle>。我不想详细展开这个行为,具体你可以阅读Java的标准。这里重要的一点是,Kotlin使用一个名为“声明端型变”(Declaration-site variance)的功能解决了这个问题。

如果给addAll方法声明中的MutableList参数添加out修饰器,编译器就允许它接受一个Truck对象的列表。Kotlin网站上给出了这种行为的解释():“用‘聪明’的词来说,类C在参数T中是协变的(covariant)吗,或者说T是个协变类型参数。你可以认为C是T的生产者,而不是T的消费者。”

class Repository<T> {    var l: MutableList<T> = ArrayList()    fun addAll(objects: MutableList<out T>) {        l.addAll(objects)    }    fun add(o: T) {        l.add(o)    }}fun main(args: Array) {    val r = Repository<Vehicle>()    var l1: MutableList<Truck> = ArrayList()    l1.add(Truck())    r.addAll(l1)    println("${r.l.size}")}

 数据类

你也许知道Java的数据类“POJO”(Plain Old Java Object)。根据Java的最佳实践,这种类应当定义getter、setter、hashCode和equals方法,还要定义toString方法供日志功能使用。即使是只有几个字段的简单类,这种实现也会占用很多篇幅。如下所示(代码是Eclipse IDE自动生成的):

public class Person {  private Integer id;  private String firstName;  private String lastName;  private int age;  public Person(Integer id, String firstName, String lastName) {    this.id = id;    this.firstName = firstName;    this.lastName = lastName;  }  public Integer getId() {    return id;  }  public void setId(Integer id) {    this.id = id;  }  public String getFirstName() {    return firstName;  }  public void setFirstName(String firstName) {    this.firstName = firstName;  }  public String getLastName() {    return lastName;  }  public void setLastName(String lastName) {    this.lastName = lastName;  }  public int getAge() {    return age;  }  public void setAge(int age) {    this.age = age;  }  @Override  public int hashCode() {    final int prime = 31;    int result = 1;    result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());    result = prime * result + ((id == null) ? 0 : id.hashCode());    result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());    return result;  }  @Override  public boolean equals(Object obj) {    if (this == obj)      return true;    if (obj == null)      return false;    if (getClass() != obj.getClass())      return false;    Person other = (Person) obj;    if (firstName == null) {      if (other.firstName != null)        return false;    } else if (!firstName.equals(other.firstName))      return false;    if (id == null) {      if (other.id != null)        return false;    } else if (!id.equals(other.id))      return false;    if (lastName == null) {      if (other.lastName != null)        return false;    } else if (!lastName.equals(other.lastName))      return false;    return true;  }  @Override  public String toString() {    return "Person [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";  }}

为了避免在POJO类中添加太多代码,可以使用Lombok项目。这个项目提供了一系列可以用在类上的注解,来提供getter、setter、equals、hashCode等方法的实现。

它还可以用@Data对类进行注解,能一次性提供@ToString、@EqualsAndHashCode、@Getter、@Setter和@REquiredArgsConstructor的功能。因此,使用Lombok和@Data,POJO会变成这样——假设你不需要带参数的构造函数的话:

@Datapublic class Person {  private Integer id;  private String firstName;  private String lastName;  private int age;}

在Java应用程序中加入并使用Lombok很简单,所有主流的IDE都支持,但Kotlin本身就解决了这个问题。

它提供了“数据类”功能,只需在类定义后面加入data关键字即可。编译器会自动根据主构造方法中定义的属性生成以下方法:

  • toString()方法;

  • 按照属性的定义顺序,依次生成相应的componentN()函数;

  • copy()函数。

因为Kotlin内部会针对可改变的属性(用var定义的属性)生成默认的getter和setter,为只读属性(用val定义的属性)生成getter,因此Person这个Java POJO用Kotlin来实现就非常简单:

data class Person(val firstName: String, val lastName: String, val id: Int) {    var age: Int = 0}

值得一提的是,编译器在自动生成函数时只会使用主构造函数中定义的属性。所以,类内部定义的age字段不会被toString、equals、hashCode和copy使用。

 测试方法的名称

现在我们来实现一些测试用例,来证明第二步中的功能可以正常工作。下面三个测试会比较两个对象,它们的age属性拥有不同的值,还会试图将同一个对象添加到Java的HashSet中两次,并检查数据类的componentN方法能否按照正确的顺序返回属性。

@Test fun `Test person equality excluding "age" property`() {  val person = Person("John", "Smith", 1)  person.age = 35  val person2 = Person("John", "Smith", 1)  person2.age = 45  Assert.assertEquals(person, person2)}@Test fun `Test person componentN method for properties`() {  val person = Person("John", "Smith", 1)  Assert.assertEquals("John", person.component1())  Assert.assertEquals("Smith", person.component2())  Assert.assertEquals(1, person.component3())}@Test fun `Test adding and getting person from a Set`() {  val s = HashSet()  val person = Person("John", "Smith", 1)  var added = s.add(person)  Assert.assertTrue(added)  added = s.add(person)  Assert.assertFalse(added)}

从上面的代码片段可以看到,Kotlin可以在方法名中使用空格,只需用反引号括起来即可。这个功能可以给测试用例起个描述性的名字,在执行过程中可以清晰地看到用例的执行过程,从而能精确地得知测试的状态。

扩展

我们来考虑这种情况:我们有一个函数库,里面包含了不可改变的类定义,但我们需要给它添加一些方法。在Java中有几种方法能实现这个需求。我们可以继承已有的类并实现新方法,可以通过其他方式实现,比如利用修饰器模式。

现在,我们假设有下面的Java类,包含人员的列表,并对外提供了getter和setter:

public class Organization {  private List<Person> persons;  public List<Person> getPersons() {    return persons;  }  public void setPersons(List<Person> persons) {    this.persons = persons;  }}

如果我想添加一个方法,能给列表添加一个Person对象,我必须继承现有的Organization类,并在继承类中实现新方法。

public class OrganizationExt extends Organization {  public void addPerson(Person person) {    getPersons().add(person);  }}

Kotlin提供了一种无须从基类继承就能扩展已有类的方式,即一种特殊的定义,称为扩展(extensions)。

下面是在Kotlin中定义与Java类似的Organization类的方式。因为Kotlin将简单的List类视作不可改变,因此我们需要使用MutableList来定义。

class Organization(val persons: MutableList = ArrayList()) {}

如下可以很容易地扩展出addPerson方法。扩展是静态解析的,而且它们不会修改被扩展的类。

class OrganizationTest {    fun Organization.addPerson(person: Person) {        persons.add(person)    }    @Test    fun testExtension() {        val organization = Organization()        organization.addPerson(Person("John", "Smith", 1))        Assert.assertTrue(organization.persons.size == 1)    }}

字符串模板

下面这个Java中没有的功能你肯定会喜欢:

println("Organization ${organization.name} with ${organization.persons.size} persons")

结论

当然,Java和Kotlin之间还有其他区别。这只是我个人喜欢的Java中没有的特性。你可以从GitHub上(https://github.com/piomin/sample-kotlin-playground.git)找到文中的源代码。

原文:https://dzone.com/articles/5-things-you-will-like-in-kotlin-as-a-java-develop

作者:Piotr Minkowski,Java开发者,架构师。热衷于与Java有关的新趋势和新技术,作品:《Mastering Spring Cloud》。

译者:弯月,责编:胡巍巍

微信改版了,

想快速看到CSDN的热乎文章,

赶快把CSDN公众号设为星标吧,

打开公众号,点击“设为星标”就可以啦!

征稿啦

CSDN 公众号秉持着「与千万技术人共成长」理念,不仅以「极客头条」、「畅言」栏目在第一时间以技术人的独特视角描述技术人关心的行业焦点事件,更有「技术头条」专栏,深度解读行业内的热门技术与场景应用,让所有的开发者紧跟技术潮流,保持警醒的技术嗅觉,对行业趋势、技术有更为全面的认知。

如果你有优质的文章,或是行业热点事件、技术趋势的真知灼见,或是深度的应用实践、场景方案等的新见解,欢迎联系 CSDN 投稿,联系方式:微信(guorui_1118,请备注投稿+姓名+公司职位),邮箱(guorui@csdn.net)。

推荐阅读:

  • 非 Java、C、Python,我使用的第一门计算机语言是它!

  • 世界首个 AI 公园诞生;马化腾:养活 QQ 最痛苦;iphone 翻新机降价 | 极客头条

  • 全面梳理百度世界大会,李彦宏又新吹了几个牛!

  • 她说:真的,没事别嫁程序员

  • 学习这么多算法到底在解决哪些问题?深度学习之外,我们要选择谁?

  • 我是如何从一个新闻狗转行成为程序猿的?

真相:Java 开发者钟爱 Kotlin 的五个原因相关推荐

  1. 从Java到Kotlin(五)

    函数与Lambda表达式 目录 一.函数声明与调用 二.参数和返回值 三.单表达式函数 四.函数作用域 五.泛型函数 六.尾递归函数 七.中缀表示法 八.Lambda表达式的语法 九.高阶函数与Lam ...

  2. nc65 单据非向导开发 源代码_最受Java开发者喜爱的5款开发工具

    在TIOBE 编程语言排行中,Java始终排在前三名,现今有700万到1000万的Java开发人员.许多应用程序的所有代码都是用Java编写的,这意味着集成开发环境(IDE)很重要,因为它是开发人员编 ...

  3. Java 8 八年不倒、IntelliJ IDEA 力压 Eclipse,2022 年 Java 开发者都在用什么?

    整理 | 苏宓 出品 | CSDN(ID:CSDNnews) 技术更新迭代的速度,让很多开发者被动地开启了奋起直追的模式.但是在某些领域或技术层面,也永远不要小看技术人的「念旧」情结,譬如 2009 ...

  4. java 搭建企业应用框架_溯源微服务开发体系:一位Java开发者的转型思考

    作者丨赵钰莹 简单来说,微服务是将大型单体应用程序和服务拆分为数个甚至数十个微服务,可扩展单个组件而不是整个应用程序堆栈,从而满足服务等级协议.然而,这个过程涉及很多问题需要解决,比如拆分原则.容量规 ...

  5. 第七十期:2019年度Java开发者路线图

    本文将向大家展示了一张Java开发者路线图.该路线图在保持简单可行的基础上,介绍了各种具有业界标准.且方便多数人遵循的工具和程序库. 作者:陈峻 许多Java开发人员都希望通过某种Java成长路线图, ...

  6. 优秀Java开发者都在看的书籍

    前言介绍: Java作为企业级应用开发的首选,深受互联网大厂的青睐.对于求职找工作的朋友来说,Java可能仍然是后端工程师的优选,虽然Python热度不断上升,但是Java作为老牌语言,在企业中的地位 ...

  7. Java开发者薪资最低?程序员只能干到30岁?国外真的没有996?Intellij真的比Eclipse受欢迎?

    Stack Overflow作为全球最大的程序设计领域的问答网站,每年都会出据一份开发者调查报告.近日,Stack Overflow公布了其第9次年度开发者调查报告(https://insights. ...

  8. 面向java开发者的函数式编程_函数式编程让你忘记设计模式

    本文是一篇<Java 8实战>的阅读笔记,阅读大约需要5分钟. 有点标题党,但是这确实是我最近使用Lambda表达式的感受.设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让 ...

  9. Google 宣布 Kotlin-first 已四年,为什么 Java 开发者仍不买账?

    [CSDN 编者按]2017 年 Google I/O 大会上,Google 重磅宣布 Kotlin 正式成为 Android 的开发一级语言,然而,如今经过四年的时间,据SlashData 最新发布 ...

最新文章

  1. 一些JSON相关的函数
  2. 2021人工神经网络第二次作业要求
  3. Linux学习整理(一)
  4. python 类-Python 变量类型
  5. simulink仿真设置
  6. 从零开始入门 K8s:深入剖析 Linux 容器
  7. PHP如何添加变量 $_SERVER
  8. 鸿蒙系统能否推广,鸿蒙系统凭实力占市场,无需通过禁止安卓系统来推广
  9. 漫谈边缘计算(一):边缘计算是大势所趋
  10. 无心剑中译阿齐姆·普雷姆吉《苦干加巧干》
  11. 光标是停在文本框文字的最后
  12. af_netlink_Linux Netlink通信机制详解(上)
  13. ASP.Net学习笔记004--基于ashx方式的ASP.Net开发1
  14. MYSQL 解压版5.7.12安装和客户端连接。
  15. 网站生成APP源代码 Flutter项目 带控制端
  16. Ice_cream's world I( 并查集 + 判环 )
  17. bgp状态idle什么原因_BGP - 2,BGP报文和BGP状态(转)
  18. c语言编文曲星游戏,关于汉诺塔游戏的思路(就是最早文曲星上移盘子的那个)...
  19. kaldi的vad计算
  20. 2018-09-11-二手车交易平台系统

热门文章

  1. 力扣--36有效的数独
  2. 编译器、Make和CMake之间的关系
  3. 用Postwoman搭建团队使用的API调试工具
  4. 中国1,2,4-三氟苯市场趋势报告、技术动态创新及市场预测
  5. 捆网包裹行业调研报告 - 市场现状分析与发展前景预测
  6. 超声波机行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  7. Java static、 final修饰符
  8. “年薪 45 万美元的 Netflix 开发工作,我不要了”
  9. 代码中大量的if/else,你有什么优化方案?
  10. 一道头条算法题,一种不为人知的解法!