玖叶教程网

前端编程开发入门

Kotlin官方文档翻译,类和对象:属性、接口、可见性修饰

feintkotlin

以下内容皆有feint进行翻译,不定期进行更新。翻译若有不正确的地方,欢迎在评论中指出。

属性和字段

声明属性

Kotlin中的类可以包含属性。 这些属性可以使用var关键字定义成可修改的,或是使用val关键字定义为只读的。

class Address {

var name: String = ...

var street: String = ...

var city: String = ...

var state: String? = ...

var zip: String = ...

}

我们可以简单的通过名字来使用一个属性, 就像Java中的字段一样:

fun copyAddress(address: Address): Address {

val result = Address() // Kotlin中没有 'new' 关键字

result.name = address.name // 访问器被调用

result.street = address.street

// ...

return result

}

Getters and Setters

声明一个属性的完整语法是:

var <propertyName>[: <PropertyType>] [= <property_initializer>]

[<getter>]

[<setter>]

初始化器, getter 和 setter 都是可选的. 如果可以从初始化器推断的话,那么属性的类型也是可选的 (或是像下面一样,从getter的返回类型中获取).

例如:

var allByDefault: Int? // 错误:需要显示的初始化器,getter 和 setter 默认是隐式的。

var initialized = 1 // 类型为 Int,默认的 getter 和 setter

声明只读属性的完整语法和可修改属性有以下两方面不同: 它以 val 开始而不是 var 并且不允许拥有 setter:

val simple: Int? // Int类型, 默认的 getter, 必须在构造函数中初始化

val inferredType = 1 // Int类型和一个默认的 getter

我们可以在属性声明的内部自定义属性访问器,和普通的函数十分相似。 这儿有一个自定义 getter 的例子:

val isEmpty: Boolean

get() = this.size == 0

一个自定义的 setter 看起来是这样子的:

var stringRepresentation: String

get() = this.toString()

set(value) {

setDataFromString(value) // 解析字符串并赋值给其他属性

}

这里为了方便,将 setter 的参数的名字取作 value, 但你可以选择一个自己更喜欢的名字。

从 Kotlin 1.1 开始, 属性的类型如果能从 getter 推断出来的话,则可以选择将其隐藏:

val isEmpty get() = this.size == 0// 为 Boolean 类型

如果你想改变一个属性的可见性,或是为它添加注解, 但是不需要改变默认的实现, 你可以定义访问器时不去定义它的主体部分:

var setterVisibility: String = "abc"

private set //setter 是私有的并且拥有默认的实现

var setterWithAnnotation: Any? = null

@Inject set // 使用 Inject setter 标注

辅助字段(backing fields)

Kotlin中的类不能拥有字段。 然而,有时候当我们使用一个属性访问器时必须有一个辅助字段。因为这些原因, Kotlin提供了一个自动生成的辅助字段,并可以使用 field 标识进行访问:

var counter = 0 // 初始化的值被直接写入到 field 中

set(value) {

if (value >= 0) field = value

}

field 标识仅仅能在属性访问器中使用。

如有有一个访问器使用了默认的实现,属性就会生成一个辅助字段,或是有一个自定义的访问器通过 field 标识引用它。

例如,下面这种情况就没有辅助字段:

val isEmpty: Boolean

get() = this.size == 0

辅助属性

如果你想做的事不符合这条 "隐式辅助字段" 的规则,你可以退一步使用 辅助字段:

private var _table: Map<String, Int>? = null

public val table: Map<String, Int>

get() {

if (_table == null) {

_table = HashMap() // 类型参数被推断出来

}

return _table ?: throw AssertionError("Set to null by another thread")

}

从各个方面来说, 这正好和Java中是一样的,因为通过默认的getter 和 setter 访问私有属性的过程会被优化,而不产生函数调用的开销。

编译时常量

当属性的值是在编译时确定的可以被标记为 编译时常量,这种属性使用 const 进行修饰。这种属性需要完全满足一下几种要求:

  • 位于顶层或是一个对象的成员

  • 使用String 或者原声类型的值进行初始化

  • 没有自定义 getter

属性也可在注解中进行使用:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

延迟初始化属性

一般来说, 属性声明后必须在构造函数中初始化一个非空类型。 然而,公平的说这样子往往是很不方便的。 举个例子, 属性可以通过独立的注入进行初始化, 或是通过 unit test 中的 setup 方法。在这些情况下, 你无法在构造函数中提供非空构造器, 但是你仍然想引用类内部的属性和防止空值检验。

为了解决这种情况,你可以使用 lateinit 修饰符标记那个属性:

public class MyTest {

lateinit var subject: TestSubject

@SetUp fun setup() {

subject = TestSubject()

}

@Test fun test() {

subject.method()// 直接使用

}

}

这个修饰符只能用在类体内部使用 var 声明的属性 (不在主构造器中), 并且不能有自定义的 getter 和 setter。 属性的类型必须是非空的,它还不能是原生类型。

在被初始化之前访问一个 lateinit 属性会抛出一个特定的以异常:" clearly identifies the property being accessed and the fact that it hasn't been initialized"(属性在未被初始化的情形下被访问)。

属性继承

参照 属性重载

属性委托

绝大多数公有类型的属性都是简单的从一个辅助字段中读取(或写入。另一方面,通过自定义 getters and setters 可以实现属性的任意行为。介于两者之间的,属性可能有一些公共的工作模式。例如: lazy values、通过一个给定的 key 从 map 中读取、访问一个数据库、访问时通知监听器等等。

这些公有的行为可以通过使用 属性委托 实现成库

接口

Kotlin中的接口和Java8非常相似。他们包含抽象方法的声明, 以及方法的实现。和抽象类不同之处在于接口不能保存状态。它们可以拥有属性,但必须是抽象的或是提供访问器的实现。

使用interface关键字来定义一个接口:

interface MyInterface {

fun bar()

fun foo() {

// optional body

}

}

接口实现

一个类或对象可以实现一个或多个接口。

class Child : MyInterface {

override fun bar() {

// body

}

}

接口中的属性

你可以在接口中声明属性。 接口中声明的属性可以是抽象的, 或是提供访问器的实现。 接口中声明的属性没有辅助(backing)字段, 所以不能在接口中声明的访问器里引用它们。

interface MyInterface {

val prop: Int // 抽象

val propertyWithImplementation: String

get() = "foo"

fun foo() {

print(prop)

}

}

class Child : MyInterface {

override val prop: Int = 29

}

解决重载冲突

当我们在父类型列表中声明了很多类型时, 可能会发生继承的同一种方法却拥有不只一种实现的状况。 例如:

interface A {

fun foo() { print("A") }

fun bar()

}

interface B {

fun foo() { print("B") }

fun bar() { print("bar") }

}

class C : A {

override fun bar() { print("bar") }

}

class D : A, B {

override fun foo() {

super<A>.foo()

super<B>.foo()

}

override fun bar() {

super<B>.bar()

}

}

接口 A 和 B 都声明了函数 foo() 和 bar()。 它们都实现了 foo(),但仅仅只有 B 实现了 bar() (bar() 在 接口 A 中没有标记为抽象, 因为这在接口中是默认的, 前提是该函数没有函数体)。 现在,如果我们从 A 派生一个实体类 C, 显而易见的,我们需要虫子 bar() 函数并且提供一个实现。

然而, 如果我们从 A 和 B 派生出 D , 我们需要实现从多个接口继承的所有方法。 这条规则适用于我们继承的单一实现的 (bar()) 方法和多实现的 (foo()) 方法。

可见性修饰符

类、对象、接口、构造函数、函数、属性和它们的setter都能拥有 可见性修饰符。(getter 的可见性一般和属性是一样的。)在Kotlin中一共有四种可见性修饰符: private, protected, internal 和 public。在没有任何显示的修饰符的时候,默认的可见性是 public.

接下来,请找出它们对于不同类型声明的作用域的详细说明。

函数、属性和类、对象和接口都能在“顶级”声明,也就是说直接在一个包里面声明:

// file name: example.kt

package foo

fun baz() {}

class Bar {}

  • 如果你没有明确任何可见性修饰符,默认是使用 public , 这意味这个声明到处都是可见的;

  • 如果你将某个声明标记为 private, 他只能在包含这个声明的文件内部可见;

  • 如果你将他标记为 internal, 它在同一个 模块的任何地方都是可见的;

  • protected 不适合顶级声明。

举个例子:

// file name: example.kt

package foo

private fun foo() {} // 在example.kt内部可见

public var bar: Int = 5 // 该属性到处可见

private set // setter在example.kt内部可见

internal val baz = 6// 在同一个模块内部可见

类和接口

类的内部成员的声明:

  • private — 意味着仅仅在类内部可见 (包括它的所有成员);

  • protected — 在 private 的基础上加上子类也可见;

  • internal — 在同一模块内部 能看见类的声明,也就能看见 internal 成员;

  • public — 在任何类可见的地方,其 public 成员也都可见。

注意: Java的使用者: Kotlin中内部类的私有成员对于外部类是不可见的。

如果你重载了一个 protected 成员并且没有显示的标明可见性,重载的成员的可见性依旧是 protected 。

举个例子:

open class Outer {

private val a = 1

protected open val b = 2

internal val c = 3

val d = 4// 默认是public

protected class Nested {

public val e: Int = 5

}

}

class Subclass : Outer() {

// a 不可见

// b, c 和 d 可见

// Nested 和 e 可见

override val b = 5 // 'b' 是 protected

}

class Unrelated(o: Outer) {

// o.a, o.b 不可见

// o.c 和 o.d 是可见的 (同一个模块)

// Outer.Nested 是不可见的, 并且 Nested::e 同样不可见

}

构造函数

想要确定一个类的主构造函数的可见性,请使用以下语法(注意:你需要显示的添加constructor关键字):

class C private constructor(a: Int) { ... }

这个构造函数的私有的。 默认情况下,所有构造函数都是 public, 只要是类可见的地方它也同样可见 (也就是说,一个 internal 类的构造函数只在同一个模块内可见 )。

局部声明

局部变量、函数和类都没有可见性修饰符

模块

internal 可见性修饰符意味着,在同一个模块内的成员可见。 更加具体点,一个模块是经过编译了的Kotlin文件的集合:

  • 一个 IntelliJ IDEA 模块;

  • 一个 Maven 工程;

  • 一个 Gradle 源码集合;

  • 一个 Ant 任务编译的文件集合。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言