Basic data types
digital
Type | Bit Width |
---|---|
Double | 64 |
Float | 32 |
Long | 32 |
Int | 32 |
Short | 16 |
Byte | 8 |
The literal constants
- Decimal number: 123
- The Long type is marked with a capital L: 123L
- Hexadecimal :0x0F
- Binary: 0 b0001011
- Default Double:123.5, 123.5e10
- Float is marked with f or f: 123.5F
Underscores in numeric literals (since 1.1)
val oneMillion = 1 _000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
Copy the code
Note:
- There is no implicit extension conversion for numbers (e.g
Java
In theint
We can implicitly convert tolong
) kotlin
Octal is not supportedKotlin
Middle characters are not numbers
character
Characters are represented by the type Char. They can’t be treated as numbers
fun check(c: Char) {
if (c == 1) { // Error: type incompatible
/ /...}}Copy the code
Each number type supports the following conversion
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
Boolean
Boolean is represented by Boolean type and has two values: true and false. Empty reference booleans will be boxed if needed. The built-in Boolean operations are:
- | | – logical or short circuit
- && — Short circuit logic and
- ! Non – logic
An array of
The control flow
If expression
In Kotlin, if is an expression, that is, it returns a value. So there is no need for ternary operators (condition? Then: otherwise), because plain if would do the job.
// Traditional usage
var max = a
if (a < b) max = b
// With else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// as an expression
val max = if (a > b) a else b
Copy the code
An if branch can be a block of code, with the final expression as the value of that block:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
Copy the code
If you use if as an expression rather than a statement (for example, returning its value or assigning it to a variable), the expression needs to have an else branch.
when
When replaces the Java switch operator. Its simplest form is as follows
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else- > {// Notice this block
print("x is neither 1 nor 2")}}Copy the code
Like if as an expression, when can be used as either an expression or a statement. If when is used as an expression, there must be an else branch, unless the compiler can detect that all possible cases have been covered.
The while loop
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while(y ! =null) // y is visible here
Copy the code
In the loopBreak
withcontinue
return
The default is to return from the function that most directly surrounds it or from an anonymous function.break
Terminate the loop that most directly surrounds it.continue
Continue the next cycle that most directly surrounds it.
function
Declaration of functions
Functions in Kotlin are declared using the fun keyword:
fun double(x: Int): Int {
return 2 * x
}
Copy the code
Define a function
- A function that takes two Int arguments and returns Int:
fun sum(a: Int, b: Int): Int {
return a + b
}
Copy the code
- Functions that automatically infer expressions as function bodies and return value types:
fun sum(a: Int, b: Int) = a + b
Copy the code
- Function returns a meaningless value,
Unit
It’s a type that has only one valueUnit
The type of. This value does not need to be returned explicitly:
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")}Copy the code
Note:
- When a function has a large number of arguments or default arguments, the function can be called by naming the arguments.
- When a function call mixes positional and named arguments, all positional arguments precede the first named argument
- In the call
Java
Function cannot use named parameter syntax becauseJava
Bytecode does not always reserve the names of function arguments;
Function scope
In Kotlin functions can be declared at the top of a file, without the need to create a class to hold a function as Java does. In addition to top-level functions, functions in Kotlin can also be declared in local scopes, as member functions, and as extension functions.
Local function
Kotlin supports local functions, where one function is inside another function:
fun count(a){
var count =0;
fun innerCount(a){
print(count)
}
}
Copy the code
Local functions can access local variables of external functions (that is, closures).
A member function
A member function is a function defined inside a class or object:
class Sample(a){
fun foo(a) { print("Foo")}}Copy the code
Member functions are called in dot notation:
Sample().foo() // Create an instance of class Sample and call foo
Copy the code
Generic function
Functions can have generic arguments, specified by using Angle brackets before the function name:
Fun <T> List(item: T): List<T> {...... }Copy the code
Inline function
Extension function
Extend the new functionality of a class without inheriting the class or using any type of design pattern like decorator. To declare an extension function, we need to prefix it with a receiver type, that is, the type to be extended.
Higher-order functions and Lambda expressions
Higher-order functions are functions that use functions as arguments or return values.
Function types
- All function types have A list of parameter types enclosed in parentheses and A return type :(A, B) -> C represents A function type that takes A and B arguments and returns A value of type C. The argument type list can be empty, for example () -> A. The Unit return type cannot be omitted.
- Function types can have an additional receiver type, which is specified before the dot in the notation: type A.(B) -> C represents A function that can be called on A receiver object of TYPE A with A type B argument and return A value of type C. Functional literals with receivers are usually used with these types.
- Suspended functions are a special class of function types that have one representation
suspend
Modifier, for examplesuspend () -> Unit
orsuspend A.(B) -> C
Coroutine correlation.
Function type notation optionally includes function parameter names: (x: Int, y: Int) -> Point. These names can be used to indicate the meaning of the parameters.
Function type instantiation
- A code block that uses a function numeric value, in one of the following forms:
- Lambda expressions:
{ a, b -> a + b }
. - Anonymous functions:
fun(s: String): Int { return s.toIntOrNull() ?: 0 }
- Lambda expressions:
Function literals with receivers can be used as values of function types with receivers
- Callable references with existing declarations:
- Top-level, local, member, extension function:
: : isOdd, String: : toInt
. - Top-level, member, extended properties:
List<Int>::size
. - Constructor:
::Regex
- Top-level, local, member, extension function:
This includes callable references to bindings that point to specific instance members: foo::toString
- Example of a custom class that implements a function-type interface:
class IntTransformer: (Int) -> Int {
override operator fun invoke(x: Int): Int = TODO()
}
val intFunction: (Int) -> Int = IntTransformer()
Copy the code
With sufficient information, the compiler can infer the function type of the variable:
val a = { i: Int -> i + 1 }
Copy the code
Function types with and without a receiver are interchangeable with non-literals, where the receiver can substitute for the first argument, and vice versa. For example, values of type (A, B) -> C can be passed or assigned to places where A.(B) -> C is expected, and vice versa:
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFun // OK
fun runTransformation(f: (String, Int) -> String): String {
return f("hello".3)
}
val result = runTransformation(repeatFun) // OK
Copy the code
Note that function types without receivers are inferred by default, even though variables are initialized by extending function references. If you want to change this, specify the variable type explicitly.
Lambda expressions
Lambda expressions and anonymous functions are functionally literal, i.e., functions that are not declared but are immediately passed as expressions. Consider the following example:
max(strings, { a, b -> a.length < b.length })
Copy the code
The function Max is a higher-order function that takes a function as a second argument. Its second argument is an expression that is itself a function, the function literal, equivalent to the following named function:
fun compare(a: String, b: String): Boolean = a.length < b.length
Copy the code
Lambda expression syntax
The full syntax of a Lambda expression is as follows:
val sum = { x: Int, y: Int -> x + y }
Copy the code
Lambda expressions are always enclosed in curly braces, full syntactic parameter declarations are enclosed in curly braces, with optional type annotations, and the function body is followed by a -> symbol. If the inferred return type of the lambda is not Unit, then the last (or possibly single) expression in the body of the lambda is treated as the return value. If we left out all the optional annotations, it would look like this:
val sum: (Int, Int) -> Int = { x, y -> x + y }
Copy the code
Passing a lambda expression to the last argument: In Kotlin there is a convention that if the last argument to a function accepts the function, the lambda expression passed as the corresponding argument can be placed outside the parentheses:
val product = items.fold(1) { acc, e -> acc * e }
Copy the code
If the lambda expression is the only argument when called, the parentheses can be omitted entirely:
run { println("...")}Copy the code
Tail recursive function
Functions can have generic arguments, specified by using Angle brackets before the function name:
Fun <T> List(item: T): List<T> {...... }Copy the code
Classes and objects
The members of the class
- Constructor and initializer block
- function
- attribute
- Nested and inner classes
- Object statement
The constructor
Kotlin uses the keyword class to declare classes
class Invoice {... }Copy the code
On the JVM, if all arguments to the main constructor have default values, the compiler generates an additional no-parameter constructor
Properties and Fields
- Attributes can use keywords
var
Declare mutable, otherwise use read-only keywordsval
.- The use of read-only attributes
val
Began to replacevar
- Read-only attributes are not allowed
setter
. To use an attribute, you simply refer to it by name
- The use of read-only attributes
- Complete syntax for declaring an attribute:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Copy the code
getter
Always have the same visibility as the property,setter
Visible permissions cannot be higher than properties.private
Class attributes are not generated by defaultgetter / setter
, all access to it is direct access, once you have customgetter / setter
When you visit, you have to go throughgetter / setter
. (You can view it in the generated Java file)
Behind the field
What are the behind the scenes fields?
There is a corresponding field in the JVM class attribute. You can view the files generated in the JVM by going to Android Studio’s Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile.
In the following case, there is no background field
val isEmpty: Boolean
get(a) = this.size == 0
Copy the code
This case is compiled directly without the isEmpty field:
public final boolean isEmpty(a) {
return this.size == 0;
}
Copy the code
If there are fields behind the scenes:
-
If at least one accessor for a property uses the default implementation, or if a custom accessor references a background field through field, a background field will be generated for that property. In particular, the private property does not have a default getter/setter but does have a hidden field.
-
The field property is used in the custom getter/setter, so there must be a field behind it. This field is the “key” we use to access the field behind the scenes. Similar to it in a Lambda expression, it is not a real keyword, but has a special meaning in a particular statement and is not a keyword in any other statement.
Behind the properties
The use of behind-the-scenes attributes
Most of the time, we want to define attributes like this:
-
Externally, the property is val, which can only be read but not written.
-
Inside a class, it is represented as a var attribute, meaning that its value can only be changed inside the class.
val size get(a) = _size
private var _size:Int = 0
Copy the code
Corresponding Java code
private int _size;
public final int getSize(a) {
return this._size;
}
Copy the code
This _size property is behind the scenes
Compiler constant
Properties of known values can be marked as compile-time constants using the const modifier. These attributes need to satisfy the following conditions
- On the top floor or
The object statement
或companion object
A member of - In order to
String
Or a value of a native type - No customization
getter
inheritance
In Kotlin all classes have a common superclass, Any, which is the default superclass for classes without a supertype declaration:
Inline class
Nested classes
Seal type
Data classes
Object expressions and object declarations
Object expression
We need to create an object of a class that has made slight changes to a class without explicitly declaring a new subclass for it, which Java handles with anonymous inner classes.
view.setOnClickListener(object :View.OnClickListener{
override fun onClick(v: View?) {}})Copy the code
Multiple supertypes can be specified by a comma-separated list followed by a colon. If we need “just one object” and no special supertype, we can simply write:
fun foo(a) {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Copy the code
Note that anonymous objects can be used as types declared only in local and private scopes. If you use an anonymous object as the return type of a public function or as the type of a public property, the actual type of the function or property will be the supertype declared by the anonymous object, or Any if you don’t declare Any. Members added to anonymous objects will not be accessible
class C {
// Private function, so its return type is an anonymous object type
private fun foo(a) = object {
val x: String = "x"
}
// Public function, so its return type is Any
fun publicFoo(a) = object {
val x: String = "x"
}
fun bar(a) {
val x1 = foo().x / / no problem
val x2 = publicFoo().x // error: failed to parse reference "x"}}Copy the code
Just like Java anonymous inner classes, code in an object expression can access variables from the scope that contains it. (Unlike Java, this is not limited to final variables.)
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++
}
})
/ /...
}
Copy the code
Object statement
The singleton pattern is useful in some scenarios, and Kotlin (after Scala) makes singleton declarations easy:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
/ /...
}
val allDataProviders: Collection<DataProvider>
get(a) = / /...
}
Copy the code
Object declarations cannot be locally scoped (that is, nested directly inside a function), but they can be nested inside other object declarations or non-inner classes. This is called object declarations. And it always follows the object keyword with a name. Like variable declarations, object declarations are not expressions and cannot be used on the right side of assignment statements.
The initialization of object declarations is thread-safe.
To reference the object, we simply use its name:
DataProviderManager. RegisterDataProvider (...)Copy the code
These objects can have supertypes:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { …… }
override fun mouseEntered(e: MouseEvent) {... }}Copy the code
Associated object
Object declarations within a class can be marked with the Companion keyword:
class MyClass {
companion object Factory {
fun create(a): MyClass = MyClass()
}
}
Copy the code
Members of the companion object can be called by using only the class name as a qualifier:
val instance = MyClass.create()
Copy the code
You can omit the name of the Companion object, in which case the name Companion will be used:
class MyClass {
companion object { }
}
val x = MyClass.Companion
Copy the code
The name of the class used by itself (not a qualifier of another name) can be used to refer to the companion objects of the class (whether named or not) :
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
Copy the code
Note that even though the members of the companion object look like static members of another language, they are still instance members of the real object at run time, and, for example, interfaces can be implemented:
interface Factory<T> {
fun create(a): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(a): MyClass = MyClass()
}
}
val f: Factory<MyClass> = MyClass
Copy the code
Semantic differences between object expressions and object declarations
- Object expressions are executed (and initialized) immediately where they are used;
- Object declarations are lazily initialized when they are first accessed;
- The initialization of the associated object matches the semantics of the Java static initializer when the corresponding class is loaded (parsed).
interface
Kotlin’s interface is similar to Java 8 in that it contains both declarations of abstract methods and implementations. Unlike abstract classes, interfaces cannot hold state. It can have attributes but must be declared abstract or provide accessor implementations.
interface MyInterface {
fun bar(a)
fun foo(a) {
// Optional method body}}Copy the code
attribute
Properties declared in an interface are either abstract or provide an implementation of the accessor. Properties declared in an interface cannot have a backing field, so accessors declared in the interface cannot reference them. As we explained in the background field above, you must customize the getter for the var property and the setter for the val property.
interface MyInterface {
val prop: Int
val propertyWithImplementation: String
get(a) = "foo"
var prop2:Int
var propWithGetAndSet:String
get(a) = "prop2"
set(value) {
print(value)
}
fun foo(a) {
print(prop)
}
}
Copy the code
Inheritance of interfaces
Unlike Java, where a class or object can implement one or more interfaces, only the missing implementation is defined.
interface MyInterface2:MyInterface{
fun pritlnProps(){
println("propertyWithImplementation = "+propertyWithImplementation)
println("propWithGetAndSet = "+propWithGetAndSet)
println("prop = "+prop)
println("prop2 = "+prop2)
}
}
class A :MyInterface,MyInterface2{
override val prop: Int = 1
override var prop2: Int = 2
}
Copy the code
Resolving coverage Conflicts
Consistent with the override rules for class inheritance, the super keyword is required to specify which supertype to inherit from.
Refer to the link
- Background fields and background properties
- Scope functions Scope Funtions