Kotlin中的类是使用class
关键字来声明:
class Invoice { |
} |
class Empty |
Kotlin中的类可以有一个主构造函数和一个或多个辅助构造函数。 主构造函数是类头的一部分:它在类名后面(和可选的类型参数)。
class Person constructor(firstName: String) { |
} |
constructor
关键字:
class Person(firstName: String) { |
} |
init
关键字:
class Customer(name: String) { |
init { |
logger.info("Customer initialized with value ${name}") |
} |
} |
class Customer(name: String) { |
val customerKey = name.toUpperCase() |
} |
class Person(val firstName: String, val lastName: String, var age: Int) { |
// ... |
} |
var
)或只读(val
)。constructor
关键字是必需的,修饰符将在它之前:
class Customer public @Inject constructor(name: String) { ... } |
constructor
关键字作为前缀:
class Person { |
constructor(parent: Person) { |
parent.children.add(this) |
} |
} |
this
关键字对同一类的另一个构造函数进行委派:
class Person(val name: String) { |
constructor(name: String, parent: Person) : this(name) { |
parent.children.add(this) |
} |
} |
class DontCreateMe private constructor () { |
} |
class Customer(val customerName: String = "") |
要创建一个类的实例,需要调用类的构造函数,就像它是一个常规函数一样:
val invoice = Invoice() |
val customer = Customer("Joe Minsu") |
在嵌套类中描述了创建嵌套,内部和匿名内部类的实例。
类可以包含 -
Any
,这是一个没有父类型的类的默认超类。
class Example // Implicitly inherits from Any |
Any
不是java.lang.Object
; 特别地要说明的是,除了equals()
,hashCode()
和toString()
之外,它不具有其它任何成员函数。有关更多详细信息,请参阅Java互操作性部分。
要声明一个显式的超类型,将冒号后面的类型放在类头中:
open class Base(p: Int) |
class Derived(p: Int) : Base(p) |
如果类具有主构造函数,则可以使用主构造函数的参数(并且必须)初始化基类型。
如果类没有主构造函数,则每个辅助构造函数必须使用super
关键字初始化基类型,或者委托给另一个构造函数。 请注意,在这种情况下,不同的辅助构造函数可以调用基类型的不同构造函数:
class MyView : View { |
constructor(ctx: Context) : super(ctx) |
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) |
} |
一个类的开放(open
)注释与Java的最终结果相反:它允许其他人继承这个类。 默认情况下,Kotlin中的所有类都是final
,它对应于有效Java用法,设计和继承的文档或者禁止它。
正如前面提到的,与Java不同的是,Kotlin需要对可覆盖成员进行显式注释(称之为open
)和覆盖:
open class Base { |
open fun v() {} |
fun nv() {} |
} |
class Derived() : Base() { |
override fun v() {} |
} |
Derived.v()
需要覆盖(override
)注释。 如果缺少(override
)注释,编译器会抱错。 如果在一个函数上没有open
注释,如在Base.nv()
中,在子类中声明一个具有相同签名的方法是非法的,无论是否有覆盖(override
)注释还是没有。 在final
类(例如不使用open
注释的类)中,则禁止覆盖成员。
标记为覆盖(override
)的成员本身是打开的,即它可以在子类中被覆盖。 如果要禁止重新覆盖,请使用final
关键字:
open class AnotherDerived() : Base() { |
final override fun v() {} |
} |
覆盖属性的工作方式与覆盖方法类似; 在超类上声明,然后在派生类上重新声明的属性必须以override
替代,并且它们必须具有兼容类型。 每个声明的属性可以被具有初始化器的属性或具有getter
方法的属性覆盖。
open class Foo { |
open val x: Int get { ... } |
} |
class Bar1 : Foo() { |
override val x: Int = ... |
} |
还可以使用var
属性覆盖val
属性,反之亦然。 这是允许的,因为val
属性基本上声明一个getter
方法,并将其替换为var
,另外在派生类中声明一个setter
方法。
请注意,可以在主构造函数中使用override
关键字作为属性声明的一部分。
interface Foo { |
val count: Int |
} |
class Bar1(override val count: Int) : Foo |
class Bar2 : Foo { |
override var count: Int = 0 |
} |
在Kotlin中,实现继承由以下规则控制:如果类从其直接超类继承同一成员的多个实现,则它必须覆盖该成员并提供自己的实现(可能使用其中一个继承)。 要表示从其继承的实现的超类型,可在尖括号中使用超类型名称超级限定,例如,super<Base>
。
open class A { |
open fun f() { print("A") } |
fun a() { print("a") } |
} |
interface B { |
fun f() { print("B") } // interface members are 'open' by default |
fun b() { print("b") } |
} |
class C() : A(), B { |
// The compiler requires f() to be overridden: |
override fun f() { |
super<A>.f() // call to A.f() |
super<B>.f() // call to B.f() |
} |
} |
从B
继承A
是没有问题的,对a()
和b()
函数也没有任何问题,因为C
只继承每个这些函数的一个实现。 但是对于f()
有两个由C
继承的实现,因此必须在C
中重写f()
函数并提供自己的消除歧义的实现。
一个类和其一些成员可以被声明为抽象。 抽象成员在其类中没有实现。 请注意,不需要使用open
来注释抽象类或函数。
可以用抽象来覆盖一个非抽象的open
成员 -
open class Base { |
open fun f() {} |
} |
abstract class Derived : Base() { |
override abstract fun f() |
} |
在Kotlin中,与Java或C#不同,类没有静态(static
)方法。 在大多数情况下,建议简单地使用包级别的功能。
如果需要编写一个可以调用的函数,而不需要一个类实例,但需要访问一个类的内部(例如,一个工厂方法),则可以将其作为对象声明的一个成员编写。
更具体地说,如果在类中声明了一个伴随对象,则可以使用类名作为限定符的Java/C#中调用静态方法相同的语法来调用其成员。