291 lines
12 KiB
Plaintext
291 lines
12 KiB
Plaintext
h1. Classes and Primary constructors
|
|
|
|
Here is an example of a class declaration in [Kotlin]:
|
|
{jet}
|
|
class Example(param : Int) {
|
|
val property = param
|
|
}
|
|
{jet}
|
|
Let's start with the header that says
|
|
{jet}
|
|
class Example(param : Int) {
|
|
{jet}
|
|
This is a declaration of a class {{Example}} that has a constructor that takes one parameter of type {{Int}}. Such constructors, declared immediately in the class header, are called _primary constructors_.
|
|
|
|
To create a new instance of the class {{Example}}, one simply calls its constructor, as if it were a regular function:
|
|
{jet}
|
|
val e = Example(10)
|
|
{jet}
|
|
(Note that one does not need the *new* keyword.)
|
|
|
|
If a class does not have any constructors declared, one cannot instantiate it:
|
|
{jet}
|
|
class Some {}
|
|
|
|
val s = <error desc="Can't find a function 'Some'. Note that the class Some has no constructor">Some()</error> // Error: no constructor for Some
|
|
{jet}
|
|
*Note* that a class with no constructors must not declare any state (properties with backing fields), or inherit from classes that have constructors.
|
|
|
|
What does the primary constructor do? It executes the initializers of properties, in the order of declaration. These initializers can use the constructor's parameters. In the example above, the only thing the constructor does is initialization of {{property}}.
|
|
|
|
Additionally, one can place "anonymous initializers" in the body of a class, just surrounding pieces of code with curly braces:
|
|
{jet}
|
|
class ExampleWithAnonymousInitializer(param : Int) {
|
|
val property = HashSet<Int>()
|
|
{
|
|
property.add(param)
|
|
}
|
|
}
|
|
{jet}
|
|
Here, the primary constructor first creates a new {{HashSet}}, assigns it to {{property}} and then adds the value of {{param}} to this set.
|
|
|
|
{anchor:bean-class}
|
|
Parameters of primary constructors can be prefixed with *val* or *var* keywords to declare corresponding [properties|Properties And Fields] in-place:
|
|
{jet}
|
|
class Bean(val prop1 : Int, val prop2 : String)
|
|
{jet}
|
|
Class body is optional, and the example above is a complete declaration of a class that has two properties *prop1* and *prop2* and a primary constructor with two parameters, that initializes these properties in an obvious way.
|
|
|
|
So, it does not take you much code to declare a bean-style data class in [Kotlin], at least it is a lot less code than in Java.
|
|
|
|
h1. Class members
|
|
|
|
Classes have the following kinds of members:
|
|
* [Functions]
|
|
* [Properties|Properties And Fields]
|
|
* [Other classes|Nested classes]
|
|
* [Object declarations|Object expressions and Declarations]
|
|
|
|
h1. Inheritance
|
|
|
|
All classes in [Kotlin] have a common superclass {{Any}}, that is a default super for a class with no supertypes declared:
|
|
{jet}
|
|
class Example // Implicitly inherits from Any
|
|
{jet}
|
|
|
|
{{Any}} is not {{java.lang.Object}}; in particular, it does not have any members, not even {{equals()}}, {{hashCode}} or {{toString()}}. This does not mean that you can not call, say, {{toString()}} on any object: you can, but it will be an [extension function|Extension functions]. See [Java interoperability#Object methods] for more details.
|
|
|
|
To declare an explicit supertype, one puts it after a colon in the class header:
|
|
{jet}
|
|
open class Base(p : Int)
|
|
|
|
class Derived(p : Int) : Base(p)
|
|
{jet}
|
|
As you can see, the base type can (and must) be initialized right there, using the parameters of the primary constructor.
|
|
|
|
The *open* annotation on a class is the opposite of *Java*'s *final*: it allows others to inherit from this class. By default, all classes in [Kotlin] are *final*, which corresponds to Item 17 of _[Effective Java|http://java.sun.com/docs/books/effective]_: *Design and document for inheritance or else prohibit it*.
|
|
|
|
A class may have _many supertypes_. See [*this post*|http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/] for more details.
|
|
|
|
h2. Overriding members
|
|
|
|
As we mentioned before, we stick to making things explicit in [Kotlin]. And unlike *Java*, [Kotlin] requires explicit annotations for overridable members that we call *open* and for *overrides*:
|
|
{jet}
|
|
open class Base {
|
|
open fun v() {}
|
|
fun nv() {}
|
|
}
|
|
class Derived() : Base {
|
|
override fun v() {}
|
|
}
|
|
{jet}
|
|
The *override* annotation is required for {{Derived.v()}}. If it were missing, the compiler would complain. If there is no *open* annotation on a function, like {{Base.nv()}}, declaring a method with the same signature in a subclass is illegal, either with *override* or without it. In a *final* class (e.g. a class with no *open* annotation), *open* members are prohibited.
|
|
|
|
A member marked *override* is itself _open_, i.e. it may be overridden in subclasses. If you want to prohibit re-overriding, use *final*:
|
|
{jet}
|
|
open class AnotherDerived() : Base {
|
|
final override fun v() {}
|
|
}
|
|
{jet}
|
|
|
|
h3. (!) Wait\! How will I hack my libraries now?\!
|
|
|
|
One issue with our approach to overriding (classes and members *final* by default) is that it would be difficult to subclass something inside the libraries you use to override some method that was not intended for overriding by the library designer, and introduce some nasty hack there.
|
|
|
|
We think that this is not a disadvantage, for the following reasons:
|
|
# Best practices say that you should not allow these hacks anyway
|
|
# People successfully use other languages (C++, C#) that have similar approach
|
|
# If people really want to hack, there still are ways: in some cases it will be possible to write your hack in Java, and Aspect frameworks always work for these purposes...
|
|
|
|
h2. Traits
|
|
|
|
_Traits_ are essentially interfaces with (optional) method bodies. See [*this post*|http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/] for more details.
|
|
|
|
h2. Overriding rules
|
|
|
|
In [Kotlin], implementation inheritance is regulated by the following rule: if a class inherits many _implementations_ of the same member from its immediate superclasses, it must override this member and provide its own implementation (perhaps, using one of the inherited ones). To denote the supertype from which the inherited implementation is taken, we use *this* qualified by the supertype name in angle brackets, e.g. *this{*}{{<Base>}}:
|
|
{jet}
|
|
open class A {
|
|
open fun f() { print("A") }
|
|
fun a() { print("a") }
|
|
}
|
|
|
|
trait B {
|
|
open fun f() { print("B") }
|
|
open 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()
|
|
}
|
|
}
|
|
{jet}
|
|
It's fine to inherit from both {{A}} and {{B}}, and we have no problems with {{a()}} and {{b()}} since {{C}} inherits only _one implementation_ of each of these functions. But for {{f()}} we have _two_ implementations inherited by {{C}}, and this we _have to_ override {{f()}} in {{C}} and provide our own implementation that eliminates the ambiguity.
|
|
|
|
h2. Abstract classes
|
|
|
|
As in *Java*, a class and some of its members may be declared *abstract*. An *abstract* member does not have an implementation in its class. Thus, when some descendant inherits an abstract member, it does not count as an implementation:
|
|
{jet}
|
|
abstract class A {
|
|
abstract fun f()
|
|
}
|
|
|
|
trait B {
|
|
open fun f() { print("B") }
|
|
}
|
|
|
|
class C() : A(), B {
|
|
// We are not required to override f()
|
|
}
|
|
{jet}
|
|
Note that we do not need to annotate an *abstract* class *open* -- it goes without saying. Neither need we annotate an *abstract* function *open*.
|
|
|
|
One can *override* a non-abstract *open* member with an *abstract* one:
|
|
{jet}
|
|
open class Base {
|
|
open fun f() {}
|
|
}
|
|
|
|
abstract class Derived : Base {
|
|
override abstract fun f()
|
|
}
|
|
{jet}
|
|
|
|
h2. Overridable properties and accessors
|
|
|
|
A property may be declared *open* as well as a function. This actually means that _accessors_ of this property can be overridden:
|
|
{jet}
|
|
open class Base {
|
|
open val p : Int
|
|
get() = 1
|
|
}
|
|
class Derived : Base {
|
|
override val p : Int
|
|
get() = 2
|
|
}
|
|
{jet}
|
|
|
|
One can make individual accessors *open* and *override* them one-by-one:
|
|
{jet}
|
|
open class Base {
|
|
var p : Int
|
|
get() = 1
|
|
open set(value) { /* do nothing */ }
|
|
}
|
|
class Derived : Base {
|
|
var p : Int
|
|
/* get is inherited as it was */
|
|
override set(value) { print(value) }
|
|
}
|
|
{jet}
|
|
|
|
h1. Delegation
|
|
|
|
The [Delegation pattern|http://en.wikipedia.org/wiki/Delegation_pattern] has proven to be a good alternative to implementation inheritance, and [Kotlin] supports it natively requiring zero boilerplate code. A class {{Derived}} can inherit from a trait {{Base}} and delegate all of its public methods to a specified object:
|
|
{jet}
|
|
trait Base {
|
|
fun print()
|
|
}
|
|
|
|
class BaseImpl(val x : Int) : Base {
|
|
override fun print() { print(x) }
|
|
}
|
|
|
|
class Derived(b : Base) : Base by b
|
|
|
|
fun main() {
|
|
val b = BaseImpl(10)
|
|
Derived(b).print() // prints 10
|
|
}
|
|
{jet}
|
|
The *by*\-clause in the supertype list for {{Derived}} indicates that {{b}} will be stored internally in objects of {{Derived}} and the compiler will generate all the methods of {{Base}} that forward to {{b}}.
|
|
|
|
h1. Generic classes
|
|
|
|
See [Generics]
|
|
|
|
h1. Class objects
|
|
|
|
In [Kotlin], unlike *Java*, classes do not have *static* methods. In most cases, [namespace-level functions|Packages] form a good substitute for them, but there are a few cases when they don't. These cases involve access to class' internals (private members).
|
|
|
|
For example, to replace a constructor with a [Factory method|http://en.wikipedia.org/wiki/Factory_method_pattern], one makes the constructor *private* and provides a function that calls the constructor. But if this function in located outside the class in question, it would not have any access to the constructor.
|
|
|
|
To address this issue (and to provide some other interesting features), [Kotlin] introduces a concept of a *class object* (the closest analog in other languages would be [Companion objects in *Scala*|http://programming-scala.labs.oreilly.com/ch06.html#CompanionObjects]). Roughly speaking, a *class object* for class {{C}} is an object (in the sense of [Object declaration|Object expressions and Declarations]) that is associated to {{C}}. There may be not more than one class object for each class. A *class object* is declared inside its associated class, and thus it can access its *private* members. A *class object* for {{C}} itself is (usually) not and instance of {{C}}. For example:
|
|
{jet}
|
|
class C() {
|
|
class object {
|
|
fun create() = C()
|
|
}
|
|
}
|
|
|
|
fun main() {
|
|
val c = C.create() // C denotes the class object here
|
|
}
|
|
{jet}
|
|
|
|
At first you may think that this is just a way of grouping static members of a class together instead of mixing them with instance members: in *Java* we access static members of {{C}} by calling {{C.foo()}}, and the same happens with *class object*'s members in [Kotlin]. But in fact there is an important difference: a *class object* can have _supertypes_, and {{C}}, as an expression denotes this object as a value, so one can pass it around, say, as an argument for a function. Let's modify our example to demonstrate this:
|
|
{jet}
|
|
abstract class Factory<out T> {
|
|
fun create() : T
|
|
}
|
|
|
|
open class C() {
|
|
class object : Factory<C> {
|
|
override fun create() : C = C()
|
|
}
|
|
}
|
|
|
|
fun main() {
|
|
val factory = C // C denotes the class object
|
|
val c = factory.create()
|
|
}
|
|
{jet}
|
|
|
|
{note:title=Class object bounds are not supported yet}See the corresponding [issue|http://youtrack.jetbrains.com/issue/KT-1437].{note}
|
|
|
|
*Note* that class objects are never inherited:
|
|
{jet}
|
|
class D : C()
|
|
|
|
val d = <error desc="Class D has no class object">D</error>.create() // Error: no class object for D
|
|
{jet}
|
|
|
|
A description of some more interesting features related to *class objects* can be found in the [Generic constaints|Generics#Class objects] section.
|
|
|
|
*Note*: if you think that *class objects* are a great way of implementing singletons in [Kotlin], please see [Object expressions and Declarations].
|
|
|
|
h1. Best practices related to this feature
|
|
|
|
[Effective Java Second Edition by Joshua Bloch|http://java.sun.com/docs/books/effective/]
|
|
*Item 16*: Favor composition over inheritance
|
|
*Item 17*: Design and document for inheritance or else prohibit it
|
|
*Item 18*: Prefer interfaces to abstract classes
|
|
|
|
h1. Similar features in other languages
|
|
|
|
IDEs automatically [generate delegating methods|http://www.jetbrains.com/idea/features/code_generation.html#link1].
|
|
|
|
[Project Lombok|http://projectlombok.org/features/Delegate.html] implements delegation in *Java* with annotations.
|
|
|
|
*Scala* has traits.
|
|
|
|
*Groovy* has [Categories and Mixins|http://groovy.codehaus.org/Category+and+Mixin+transformations].
|
|
|
|
h3. What's next
|
|
|
|
* [Enum classes]
|
|
* [Nested classes]
|
|
* [Object expressions and Declarations] |