百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

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

suiw9 2024-10-28 18:32 19 浏览 0 评论

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 任务编译的文件集合。

相关推荐

nginx的反向代理(Nginx的反向代理和负载均衡)

nginxProxy代理1、代理原理反向代理服务的实现:需要有一个负载均衡设备(即反向代理服务器)来分发用户请求,将用户请求分发到后端正真提供服务的服务器上。服务器返回自己的服务到负载均衡设备。负...

Nginx UI: 更好用更现代化的Nginx 管理面板

各位铲屎官大家好,我是喵~...

性能测试之tomcat+nginx负载均衡(nginxtcp负载均衡)

nginxtomcat配置准备工作:两个tomcat执行命令cp-rapache-tomcat-8.5.56apache-tomcat-8.5.56_2修改被复制的tomcat2下con...

nginx upstream节点健康检查(nginx tcp 健康检查)

1、前提条件编译nginx时增加nginx_upstream_check_module模板git地址:https://github.com/yaoweibin/nginx_upstream_check...

Nginx 的高并发处理能力(nginx支持高并发原理)

为了实现Nginx的高并发处理能力,需要从**硬件资源**、**操作系统**、**Nginx配置**等多个方面进行优化。以下是详细的配置和示例:---...

Nginx最全详解(万字图文总结)(nginxs)

大家好,我是mikechen。Nginx是非常重要的负载均衡中间件,被广泛应用于大型网站架构,下面我就全面来详解Nginx@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题...

如何用 Nginx 实现前端灰度发布(nginx 灰度测试规则)

前言在前端开发中,灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新。通过灰度发布,我们可以测试新版本的稳定性和性能,同时收集用户反馈。今天,我们将探讨如何使用Ngi...

nginx配置优化场景-直接套用so happy!

前言(叠甲在先)Nginx是一款高性能的Web服务器,广泛应用于互联网领域。...

Nginx配置前后端服务(nginx前后端分离部署)

nginx安装完成后,可以通过命令查看配置文件nginx-t配置文件nginx.conf,是总的配置,有的人会把配置全部配置到这个文件中,如果服务很多,这个文件变得非常庞大,我见过一个配置很大的,在...

使用Nginx配置TCP负载均衡(nginx如何配置负载均衡)

假设Kubernetes集群已经配置好,我们将基于CentOS为Nginx创建一个虚拟机。...

Nginx服务器深度指南:安装、配置、优化指令超详解

在当今数字化时代,Web服务器是支撑互联网应用的关键基础设施。Nginx作为一款高性能的开源Web服务器,凭借卓越的性能、丰富的功能和出色的稳定性,在Web服务器领域占据了重要地位。无论是大型互联网公...

Nginx的配置详解(附代码)(nginx基本配置)

本篇文章给大家带来的内容是关于Nginx的配置详解(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。常用配置项在工作中,我们与Nginx打交道更多的是通过其配置文件来进行。...

Nginx配置文件详解(nginx配置文件详解带实例)

Nginx配置文件详解Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。...

从 0 到 1:构建高可用 Linux 负载均衡集群(基于 Nginx + Keepalived)

在高并发业务场景下,单台服务器往往无法支撑大量请求,因此需要使用**负载均衡(LoadBalancing)**技术来提升系统的稳定性和可用性。Nginx+Keepalived是常见的开源负载均...

配置Nginx TCP转发(nginx 接口转发)

Nginx一般用在HTTP的转发,TCP的转发大都会使用HAProxy。工作中遇到一个需求,用到了Nginx服务作为TCP转发。场景是这样,数据采集设备通过公网将数据推送到后端应用服务,服务部署在业主...

取消回复欢迎 发表评论: