Classes and Objects
- Classes and Inheritance
- Properties and Fields
- Interfaces
- Visibility Modifiers
- Extensions
- Data Classes
- Nested and Inner Classes
- Enum classes
- Object Expressions and Declarations
- Type aliases
- Inline classes
- Delegation
Classes and Inheritance
The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor etc.) and the class body, surrounded by curly braces. Both the header and the body are optional; if the class has no body, curly braces can be omitted.
class Invoice { /*...*/ }
class Empty
Constructors
primary constructor
The primary constructor is part of the class header,
class Person constructor(firstName: String) { /*...*/ }
If the primary constructor does not have any annotations or visibility modifiers, the constructor keyword can be omitted:
class Person(firstName: String) { /*...*/ }
The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks, which are prefixed with the init keyword.
During an instance initialization,the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers:
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints ${name}")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
fun main() {
InitOrderDemo("hello")
}
and the result is :
First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5
Note that parameters of the primary constructor can be used in the initializer blocks. They can also be used in property initializers declared in the class body:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
Much the same way as regular properties, the properties declared in the primary constructor can be mutable (var) or read-only (val).
If the constructor has annotations or visibility modifiers, the constructor keyword is required, and the modifiers go before it:
class Person(val firstName: String, val lastName: String, var age: Int) { /*...*/ }
class Customer public @Inject constructor(name: String) { /*...*/ }
Secondary constructors
The class can also declare secondary constructors, which are prefixed with constructor:
class Person {
var children: MutableList<Person> = mutableListOf<>()
constructor(parent: Person) {
parent.children.add(this)
}
}
If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword:
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf<>()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
Note that code in initializer blocks effectively becomes part of the primary constructor.== Delegation to the primary constructor happens as the first statement of a secondary constructor==, so the code in all initializer blocks and property initializers is executed before the secondary constructor body. Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed:
class Constructors {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor")
}
}
fun main() {
Constructors(1)
}
result is :
Init block
Constructor
NOTE: On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors.
class Customer(val customerName: String = "")
Creating instances of classes
val invoice = Invoice()
val customer = Customer("Joe Smith")
Inheritatnce
Any is superclass
Any has three methods:equals(), hashCode() and toString(). Thus, they are defined for all Kotlin classes.
class Example // Implicitly inherits from Any
Syntax of class inheritatance
By default, Kotlin classes are final: they can’t be inherited. To make a class inheritable, mark it with the open keyword.
open class Base //Class is open for inheritance
open class Base(p: Int)
class Derived(p: Int) : Base(p)
If the derived class has a primary constructor, the base class can (and must) be initialized right there, using the parameters of the primary constructor.
If the derived class has no primary constructor, then each secondary constructor has to initialize the base type using the super keyword, or to delegate to another constructor which does that. Note that in this case different secondary constructors can call different constructors of the base type:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
Overriding methods
As we mentioned before, we stick to making things explicit in Kotlin. So, Kotlin requires explicit modifiers for overridable members (we call them open) and for overrides:
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
A member marked override is itself open, i.e. it may be overridden in subclasses. If you want to prohibit re-overriding, use final:
open class Rectangle() : Shape() {
final override fun draw() { /*...*/ }
}
Overriding properties
Each declared property can be overridden by a property with an initializer or by a property with a get method.
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override val vertexCount = 4
}
You can also override a val property with a var property, but not vice versa
you can use the override keyword as part of the property declaration in a primary constructor.
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices
class Polygon : Shape {
override var vertexCount: Int = 0 // Can be set to any number later
}
Derived class initialization order
During construction of a new instance of a derived class,the base class initialization is done as the first step (preceded only by evaluation of the arguments for the base class constructor) and thus happens before the initialization logic of the derived class is run.
open class Base(val name: String) {
init { println("Initializing Base") }
open val size: Int =
name.length.also { println("Initializing size in Base: $it") }
}
class Derived(
name: String,
val lastName: String
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {
init { println("Initializing Derived") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}
fun main() {
println("Constructing Derived(\"hello\", \"world\")")
val d = Derived("hello", "world")
}
result is :
Constructing Derived("hello", "world")
Argument for Base: Hello
Initializing Base
Initializing size in Base: 5
Initializing Derived
Initializing size in Derived: 10
It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks.
Calling the superclass implementation
Code in a derived class can call its superclass functions and property accessors implementations using the super keyword:
nside an inner class, accessing the superclass of the outer class is done with the super keyword qualified with the outer class name: super@Outer:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
super.draw()
println("Filling the rectangle")
}
//override property with get method
val fillColor: String get() = super.borderColor
}
class FilledRectangle: Rectangle() {
fun draw() { /* ... */ }
val borderColor: String get() = "black"
inner class Filler {
fun fill() { /* ... */ }
fun drawAndFill() {
super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // Uses Rectangle's implementation of borderColor's get()
}
}
}
Overriding rules
open class Rectangle {
open fun draw() { /* ... */ }
}
interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
Abstract classes
open class Polygon {
open fun draw() {}
}
abstract class Rectangle : Polygon() {
abstract override fun draw()
}
Companion objects
Properties and Fields
Declaring Properties val and var
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
Getters and Setters
syntax
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
val isEmpty: Boolean
get() = this.size == 0
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
val isEmpty get() = this.size == 0 // has type Boolean
var setterVisibility: String = "abc"
private set // the setter is private and has the default implementation
var setterWithAnnotation: Any? = null
@Inject set // annotate the setter with Inject
Backing Fields
Fields cannot be declared directly in Kotlin classes. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier:
var counter = 0 // Note: the initializer assigns the backing field directly
set(value) {
if (value >= 0) field = value
}
The field identifier can only be used in the accessors of the property.
A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field identifier.
For example, in the following case there will be no backing field:
val isEmpty: Boolean
get() = this.size == 0
Backing Properties
If you want to do something that does not fit into this “implicit backing field” scheme, you can always fall back to having a backing property:
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
}
return _table ?: throw AssertionError("Set to null by another thread")
}
the more detailed understanding the backing properties ,please ref to here
Compile-Time Constants
f the value of a read-only property is known at the compile time, mark it as a compile time constant using the const modifier. Such properties need to fulfil the following requirements:
-Top-level, or member of an object declaration or a companion object.
-Initialized with a value of type String or a primitive type
-No custom getter
Such properties can be used in annotations:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
Late-Initialized Properties and Variables
Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
To handle this case, you can mark the property with the lateinit modifier:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
The modifier can be used on var properties declared inside the body of a class (not in the primary constructor, and only when the property does not have a custom getter or setter) and, since Kotlin 1.2, for top-level properties and local variables. The type of the property or variable must be non-null, and it must not be a primitive type.
Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property being accessed and the fact that it hasn’t been initialized.
if (foo::bar.isInitialized) {
println(foo.bar)
}
Interfaces
Interfaces in Kotlin can contain declarations of abstract methods, as well as method implementations. What makes them different from abstract classes is that interfaces cannot store state. They can have properties but these need to be abstract or to provide accessor implementations.
interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
class Child : MyInterface {
override fun bar() {
// body
}
}
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
Interfaces Inheritance
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName"
}
data class Employee(
// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
Resolving overriding conflicts
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()
}
}
Visibility Modifiers
Classes, objects, interfaces, constructors, functions, properties and their setters can have visibility modifiers. (Getters always have the same visibility as the property.) There are four visibility modifiers in Kotlin: private, protected, internal and public. The default visibility, used if there is no explicit modifier, is public.
Packages
Functions, properties and classes, objects and interfaces can be declared on the “top-level”, i.e. directly inside a package:
// file name: example.kt
package foo
fun baz() { ... }
class Bar { ... }
-If you do not specify any visibility modifier, public is used by default, which means that your declarations will be visible everywhere;
-If you mark a declaration private, it will only be visible inside the file containing the declaration;
-If you mark it internal, it is visible everywhere in the same module;
-protected is not available for top-level declarations.
// file name: example.kt
package foo
private fun foo() { ... } // visible inside example.kt
public var bar: Int = 5 // property is visible everywhere
private set // setter is visible only in example.kt
internal val baz = 6 // visible inside the same module
Classes and Interfaces
For members declared inside a class:
-private means visible inside this class only (including all its members);
-protected — same as private + visible in subclasses too;
-internal — any client inside this module who sees the declaring class sees its internal members;
-public — any client who sees the declaring class sees its public members.
Note that in Kotlin, outer class does not see private members of its inner classes.
If you override a protected member and do not specify the visibility explicitly, the overriding member will also have protected visibility.
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // public by default
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a is not visible
// b, c and d are visible
// Nested and e are visible
override val b = 5 // 'b' is protected
}
class Unrelated(o: Outer) {
// o.a, o.b are not visible
// o.c and o.d are visible (same module)
// Outer.Nested is not visible, and Nested::e is not visible either
}
Constructors and Local declarations
class C private constructor(a: Int) { ... }
Local variables, functions and classes can not have visibility modifiers
Modules
The internal visibility modifier means that the member is visible within the same module. More specifically, a module is a set of Kotlin files compiled together:
-an IntelliJ IDEA module;
-a Maven project;
-a Gradle source set (with the exception that the test source set can access the internal declarations of main);
-a set of files compiled with one invocation of the Ant task.
Extensions
Mainly for the Utils classes, for example StirngUtils or FileUtils etc. refor to here
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
Extensions are resolved statically
Extnestions are resolved as a final static method in the files or classes
ExtensionsTest.kt:
package com.gbd.mykttestapp
open class Shape
class Rectangle : Shape() {
fun forTest(){
TestPrint.println("for test in Rectangle")
}
}
fun Shape.getNameTest(): String {
TestPrint.println("call shape.getName")
return "Sharp"
}
fun Rectangle.getNameTest(msg: String): String {
forTest()
TestPrint.println("call Rectangle.getName")
return "Rectangle"
}
fun printClassName(s: Shape) {
TestPrint.println(s.getNameTest())
}
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
and the java code is :
public final class ExtensionsTestKt {
@NotNull
public static final String getNameTest(@NotNull Shape $this$getNameTest) {
Intrinsics.checkParameterIsNotNull($this$getNameTest, "$this$getNameTest");
TestPrint.INSTANCE.println("call shape.getName");
return "Sharp";
}
@NotNull
public static final String getNameTest(@NotNull Rectangle $this$getNameTest, @NotNull String msg) {
Intrinsics.checkParameterIsNotNull($this$getNameTest, "$this$getNameTest");
Intrinsics.checkParameterIsNotNull(msg, "msg");
$this$getNameTest.forTest();
TestPrint.INSTANCE.println("call Rectangle.getName");
return "Rectangle";
}
public static final void printClassName(@NotNull Shape s) {
Intrinsics.checkParameterIsNotNull(s, "s");
TestPrint.INSTANCE.println(getNameTest(s));
}
public static final void swap(@NotNull List $this$swap, int index1, int index2) {
Intrinsics.checkParameterIsNotNull($this$swap, "$this$swap");
Object tmp = $this$swap.get(index1);
$this$swap.set(index1, $this$swap.get(index2));
$this$swap.set(index2, tmp);
}
}
Attantion points
members always win:
fun main() {
class Example {
fun printFunctionType() { println("Class method") }
}
fun Example.printFunctionType() { println("Extension function") }
Example().printFunctionType()
}
result is :
Class method
Nullable receiver
Such extensions can be called on an object variable even if its value is null, and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
Extension properties :
no back fields, can only be defined by explicitly providing getters/setters.
val <T> List<T>.lastIndex: Int
get() = size - 1
Companion object extensions
If a class has a companion object defined, you can also define extension functions and properties for the companion object. Just like regular members of the companion object, they can be called using only the class name as the qualifier:
class MyClass {
companion object { } // will be called "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
Scope of extensions
To use such an extension outside its declaring package, we need to import it at the call site:
package org.example.declarations
fun List<String>.getLongestString() { /*...*/}
package org.example.usage
import org.example.declarations.getLongestString //import the declaration file name.
fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString()
}
Inside a class, you can declare extensions for another class. Inside such an extension, there are multiple implicit receivers - objects members of which can be accessed without a qualifier. The instance of the class in which the extension is declared is called dispatch receiver, and the instance of the receiver type of the extension method is called extension receiver.
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() { print(port) }
fun Host.printConnectionString() {
printHostname() // calls Host.printHostname()
print(":")
printPort() // calls Connection.printPort()
}
fun connect() {
/*...*/
host.printConnectionString() // calls the extension function
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString(443) // error, the extension function is unavailable outside Connection
}
In case of a name conflict between the members of the dispatch receiver and the extension receiver, the extension receiver takes precedence. To refer to the member of the dispatch receiver you can use the qualified this syntax.
class Connection {
fun Host.getConnectionString() {
toString() // calls Host.toString()
this@Connection.toString() // calls Connection.toString()
}
}
Extensions declared as members can be declared as open and overridden in subclasses. This means that the dispatch of such functions is virtual with regard to the dispatch receiver type, but static with regard to the extension receiver type.
open class Base { }
class Derived : Base() { }
open class BaseCaller {
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
}
open fun Derived.printFunctionInfo() {
println("Derived extension function in BaseCaller")
}
fun call(b: Base) {
b.printFunctionInfo() // call the extension function
}
}
class DerivedCaller: BaseCaller() {
override fun Base.printFunctionInfo() {
println("Base extension function in DerivedCaller")
}
override fun Derived.printFunctionInfo() {
println("Derived extension function in DerivedCaller")
}
}
fun main() {
BaseCaller().call(Base()) // "Base extension function in BaseCaller"
DerivedCaller().call(Base()) // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension receiver is resolved statically
}
Data Classes
We frequently create classes whose main purpose is to hold data. In such a class some standard functionality and utility functions are often mechanically derivable from the data. In Kotlin, this is called a data class
data class User(val name: String, val age: Int)
Attantion points
Automatically derives memebers
- equals()/hashCode() pair;
- toString() of the form “User(name=John, age=42)”;
- componentN() functions corresponding to the properties in their order of declaration;
- copy() function (see below).
other rules
- The primary constructor needs to have at least one parameter;
- All primary constructor parameters need to be marked as val or var;
- Data classes cannot be abstract, open, sealed or inner;
On the JVM, if the generated class needs to have a parameterless constructor, default values for all properties have to be specified
data class User(val name: String = "", val age: Int = 0)
Properties Declared in the Class Body
Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body:
data class Person(val name: String) {
var age: Int = 0
}
Copying
It’s often the case that we need to copy an object altering some of its properties, but keeping the rest unchanged. This is what copy() function is generated for. For the User class above, its implementation would be as follows:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
This allows us to write:
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
Data Classes and Destructuring Declarations
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
Nested and Inner Classes
Nested classes:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo =Outer.Nested().foo() // == 2
just like the static inner classes in java
Inner classes
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo =Outer().Inner().foo() // == 1
like the normal inner classes in java
Annonymous inner classes
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { ... }
override fun mouseEntered(e: MouseEvent) { ... }
})
Note: on the JVM, if the object is an instance of a functional Java interface (i.e. a Java interface with a single abstract method), you can create it using a lambda expression prefixed with the type of the interface:
val listener = ActionListener { println("clicked") }
Enum classes
the enum classes is mostly like the enum in java :
- Define the enum classes with the enum
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
- Initialization
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
- Anonymous Classes : enum instance also could implement the abstract method
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
- Implementing Interfaces in Enum Classes
import java.util.function.BinaryOperator
import java.util.function.IntBinaryOperator
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
PLUS {
override fun apply(t: Int, u: Int): Int = t + u
},
TIMES {
override fun apply(t: Int, u: Int): Int = t * u
};
override fun applyAsInt(t: Int, u: Int) = apply(t, u)
}
fun main() {
val a = 13
val b = 31
for (f in IntArithmetics.values()) {
println("$f($a, $b) = ${f.apply(a, b)}")
}
}
so the result is :
PLUS(13, 31) = 44
TIMES(13, 31) = 403
Enum Constants
There are two constants methods of enumCalss:
EnumClass.valueOf(value: String): EnumClass
EnumClass.values(): Array<EnumClass>
it’s possible to access the constants in an enum class in a generic way, using the enumValues() and enumValueOf() functions:
enum class RGB { RED, GREEN, BLUE }
inline fun <reified T : Enum<T>> printAllValues() {
print(enumValues<T>().joinToString { it.name })
}
printAllValues<RGB>() // prints RED, GREEN, BLUE
- Every enum constant has properties to obtain its name and position in the enum class declaration:
val name: String
val ordinal: Int
The difference between enum and sealed classes could be seen here
Object Expressions and Declarations
Sometimes we need to create an object of a slight modification of some class, without explicitly declaring a new subclass for it. Kotlin handles this case with object expressions and object declarations.
Object expressions : used as anonymous objects in java
inherits from some type
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
if a supertype has a constructor with parameters
open class A(x: Int) {
public open val y: Int = x
}
interface B { /*...*/ }
val ab: A = object : A(1), B {
override val y = 15
}
“just a object”, with no nontruvial supertypes
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
anonymous objects scope
Note that anonymous objects can be used as types only in local and private declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn’t declare any supertype. Members added in the anonymous object will not be accessible.
class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x"
}
// Public function, so the return type is Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
}
}
The code in object expressions can access variables from the enclosing scope.
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ...
}
Object declarations : used as Singleton
Singleton may be useful in several cases, and Kotlin makes it easy to declare singletons:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
Object declaration’s initialization is thread-safe and done at first access.
how to use:
DataProviderManager.registerDataProvider(...)
Such objects can have supertypes:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { ... }
override fun mouseEntered(e: MouseEvent) { ... }
}
Companion Objects :used as the static memebers in java
defination
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Members of the companion object can be called by using simply the class name as the qualifier:
val instance = MyClass.create()
The name of the companion object can be omitted, in which case the name Companion will be used:
class MyClass {
companion object { }
}
val x = MyClass.Companion
The name of a class used by itself (not as a qualifier to another name) acts as a reference to the companion object of the class (whether named or not):
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
Note that, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
val f: Factory<MyClass> = MyClass
However, on the JVM you can have members of companion objects generated as real static methods and fields, if you use the @JvmStatic annotation.
Semantic difference between object expressions and declarations
There is one important semantic difference between object expressions and object declarations:
- object expressions are executed (and initialized) immediately, where they are used;
- object declarations are initialized lazily, when accessed for the first time;
- a companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer.
Type aliases
- shorten long generic types:
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
- for function types:
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
- for inner an nested classes:
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
- Type aliases do not introduce new types. They are equivalent to the corresponding underlying types. When you add typealias
Predicate<T>
and usePredicate<Int>
in your code, the Kotlin compiler always expands it to(Int) -> Boolean
. Thus you can pass a variable of your type whenever a general function type is required and vice versa:
typealias Predicate<T> = (T) -> Boolean
fun foo(p: Predicate<Int>) = p(42)
fun main() {
val f: (Int) -> Boolean = { it > 0 }
println(foo(f)) // prints "true"
val p: Predicate<Int> = { it > 0 }
println(listOf(1, -2).filter(p)) // prints "[1]"
}
Inline classes
please ref to here , it is very clear artical of inline class in kotlin.
Delegation
Used as the delegation design pattern in java
using ‘by’ for the grammar
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b //using 'by'
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
can override a method of the delegated class
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
result is :
abc10
can’t override the members of the delegated class
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
and the result is :
BaseImpl: x = 10
Message of Derived