《Kotlin核心编程》 ——3 面 向 对 象

举报
华章计算机 发表于 2020/02/21 22:22:04 2020/02/21
【摘要】 本节书摘来自华章计算机《Kotlin核心编程》 —— 书中第3章,第3.1.1节,作者是水滴技术团队 。

下水篇

Kotlin核心

第3章 面向对象

第4章 代数数据类型和模式匹配

第5章 类型系统

第6章 Lambda和集合

第7章 多态和扩展

第8章 元编程

 

第3章

面 向 对 象

通过对上一章的阅读,相信你对Kotlin的基础语法已经有了一定的了解,本章我们会开启Kotlin中面向对象的大门。在Java中,也许你已经厌烦了重载多个构造方法去初始化一个类,或者又因设计了错误的继承关系而导致结构混乱。另外,你也肯定见识过Java中各种模板化的代码,这让程序变得臃肿。

很庆幸,在Kotlin中你将没有这些烦恼,它用合理的语言设计帮我们处理了可能会遇到的麻烦,比如方法默认参数、更严格的限制修饰符等。最后,你还将接触到Kotlin中编译器生成的更多样化的密封类、数据类,这为Kotlin从面向对象到函数式架起了另一条桥梁。我们会在下一章中进一步介绍它们的高级应用。

3.1 类和构造方法

Java是一门假设只用面向对象进行程序设计的编程语言,在Kotlin中对象思想同样非常重要(虽然它是多范式语言)。本节我们会从对象这个概念入手,结合一个鸟的例子,来学习在Kotlin中如何简洁地声明一个类和接口。

3.1.1 Kotlin中的类及接口

对象是什么?我们肯定再熟悉不过了,任何可以描述的事物都可以看作对象。我们以鸟为例,来分析它的组成。

状态:形状、颜色等部件可以看作鸟的静态属性,大小、年龄等可以看作鸟的动态属性,对象的状态就是由这些属性来表现的。

行为:飞行、进食、鸣叫等动作可以看作鸟的行为。

1. Kotlin中的类

对象是由状态和行为组成的,我们可以通过它们描述一个事物。下面我们就用Kotlin来抽象一个Bird类:

// Kotlin中的一个类

class Bird {

    val weight: Double = 500.0

    val color: String = "blue"

    val age: Int = 1

    fun fly() {} // 全局可见

}

是不是一点也不陌生?我们依然可以使用熟悉的class结构体来声明一个类。但是,Kotlin中的类显然也存在很多不同。作为对照,我们把上述代码反编译成Java的版本,然后分析它们具体的差异。

public final class Bird {

    private final double weight = 500.0D;

    @NotNull

    private final String color = "blue";

    private final int age = 1;

 

    public final double getWeight() {

        return this.weight;

    }

 

    @NotNull

    public final String getColor() {

        return this.color;

    }

 

    public final int getAge() {

        return this.age;

    }

 

    public final void fly() {

    }

}

可以看出,虽然Kotlin中类声明的语法非常近似Java,但也存在很多不同:

1)不可变属性成员。正如我们在第2章介绍过,Kotlin支持用val在类中声明引用不可变的属性成员,这是利用Java中的final修饰符来实现的,使用var声明的属性则反之引用可变。

2)属性默认值。因为Java的属性都有默认值,比如int类型的默认值为0,引用类型的默认值为null,所以在声明属性的时候我们不需要指定默认值。而在Kotlin中,除非显式地声明延迟初始化,不然就需要指定属性的默认值。

3)不同的可访问修饰符。Kotlin类中的成员默认是全局可见,而Java的默认可见域是包作用域,因此在Java版本中,我们必须采用public修饰才能达相同的效果。我们会在下一节讲解Kotlin中不同的访问控制。

2.可带有属性和默认方法的接口

在看过类对比之后,我们继续来看看Kotlin和Java中接口的差异。这一次,我们先来看一个Java 8版本的接口:

// Java 8中的接口

public interface Flyer {

    public String kind();

 

    default public void fly() {

        System.out.println("I can fly");

    }

}

众所周知,Java 8引入了一个新特性—接口方法支持默认实现。这使得我们在向接口中新增方法时候,之前继承过该接口的类则可以不需要实现这个新方法。接下来再来看看在Kotlin中如何声明一个接口:

// Kotlin中的接口

interface Flyer {

    val speed: Int

    fun kind()

    fun fly() {

        println("I can fly")

    }

}

同样,我们也可以用Kotlin定义一个带有方法实现的接口。同时,它还支持抽象属性(如上面的speed)。然而,你可能知道,Kotlin是基于Java 6的,那么它是如何支持这种行为的呢?我们将上面Kotlin声明的接口转换为Java代码,提取其中关键的代码:

public interface Flyer {

    int getSpeed();

 

    void kind();

 

    void fly();

 

    public static final class DefaultImpls {

        public static void fly(Flyer $this) {

            String var1 = "I can fly";

            System.out.println(var1);

        }

    }

}

我们发现Kotlin编译器是通过定义了一个静态内部类DefaultImpls来提供fly方法的默认实现的。同时,虽然Kotlin接口支持属性声明,然而它在Java源码中是通过一个get方法来实现的。在接口中的属性并不能像Java接口那样,被直接赋值一个常量。如以下这样做是错误的:

interface Flyer {

    val height = 1000 //error Property initializers are not allowed in interfaces

}

Kotlin提供了另外一种方式来实现这种效果:

interface Flyer {

    val height

        get() = 1000

}

可能你会对这种语法感到不习惯,但这与Kotlin实现该机制的背景有关。我们说过,Kotlin接口中的属性背后其实是用方法来实现的,所以说如果我们要为变量赋值常量,那么就需要编译器原生就支持方法默认实现。但Kotlin是基于Java 6的,当时并不支持这种特性,所以我们并不能像Java那样给一个接口的属性直接赋值一个常量。我们再来回味下在Kotlin接口中如何定义一个普通属性:

interface Flyer {

    val height: Long

}

它同方法一样,若没有指定默认行为,则在实现该接口的类中必须对该属性进行初始化。

总的来说,Kotlin的类与接口的声明和Java很相似,但它的语法整体上要显得更加简洁。好了,现在我们定义好了Bird类,接下来再来看看如何用它创建一个对象吧。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。