preface

In this chapter we will learn how to use Kotlin to declare the basic elements of any program: the concepts of variables, functions, classes, and attributes

Functions and variables

1.1 Hello World

Let’s start with a classic example: printing “Hello, world!”

fun main(args: Array<String>) { 
    println("Hello, world!")}Copy the code

What features can we see from the above code?

  • The keyword fun is used to declare a function. (Yes, Kotlin is fun.)
  • Parameter classes are written after parameter names, as are variable declarations.
  • Functions can be declared at the top of the file; you don’t have to put them in a class.
  • Arrays are classes. Unlike Java, Kotlin has no specific syntax for declaring arrays.
  • Use println instead of system.out.println. The Kotlin standard library provides wrappers for many standard Java library functions, which have a more concise syntax. Println is one of them.
  • As with many modern languages, you can omit the semicolon at the end of each line of code.

1.2 the function

1.2.1 Function Types

Having looked at a function that does not return a value, let’s look at a function that does return a value:

fun max(a: Int, b: Int): Int { 
    return if (a > b) a else b 
}
println(max(1, 2)) //2
Copy the code

We see that the return type is placed after the argument list.

Note: in Kotlin, if is an expression that returns a value. Similar to the Java ternary operator (a > b)? a : b

The function declaration begins with fun, followed by the function name, in this case Max, followed by the argument list, followed by the return type, separated by a colon.

No return type

Fun Function name (argument list) {function body}Copy the code

There are return types

Fun function name (argument list) : return type {function body}Copy the code

Statements and expressions

In Kotlin, if is an expression, not a statement. The difference between a statement and an expression is that an expression is a value that can be used as part of another expression; A statement is always a top-level element within the code block that contains it and has no value of its own. In Java, all control structures are statements, but in Kotlin, most control structures, with the exception of loops (for, do, and do/while), are expressions. Combining control structures with other expressions allows you to concisely express many common patterns.

On the other hand, assignment is an expression in Java, but becomes a statement in Kotlin. This effectively avoids confusion between comparison and assignment, which is also a source of errors.

1.2.2 Expression function body

You can make the previous function even simpler. Since the function body is made up of a single expression, we can use this expression as the full function body without the curly braces and return statement:

fun max(a: Int, b: Int): Int = if (a > b) a else b
Copy the code

If we use curly braces to express the function body, we call the function a code block. If we return an expression directly, we call it an expression body.

INTELLIJ IDEA Tip: INTELLIJ IDEA provides conversion between two different function styles: “Convert to expression body” and “Convert to block body”

Expression body functions are common in Kotlin code, not only in some simple functions, but also in many complex expressions, such as if, when, try, and so on

1.2.3 Type derivation

Our Max function can be further simplified as follows:

fun max(a: Int, b: Int) = if (a > b) a else b
Copy the code

Why doesn’t a function have a return type declaration? As a statically typed language, doesn’t Kotlin require that every expression should have a type at compile time? In fact, every variable and expression has a return type. But for functions with an expression body, the compiler can parse the expression as the function body and use its type as the return type, even if it is not written explicitly. This type of analysis is often called type inference.

Note: Omitting return types is only allowed in functions of the expression body. A function with a code block that returns a value. You must specify the return type and the return statement to display. The actual function is usually very long and may contain many return statements. Having the return type and statement displayed can help you quickly know what is returned.

1.3 variable

In Java, you declare variables with types. But in Kotlin, the types of many variables can be omitted, so in Kotlin you start with the keyword, then the variable name, and then the type (optionally). Omission type:

val question = "The Ultimate Question of Life, the Universe, and Everything"
val answer = 42
Copy the code

Display type:

val answer: Int = 42
Copy the code

Cannot omit type: variable has no initializer and needs to be displayed

val answer:Int
answer = 42
Copy the code

Mutable variables and immutable variables

  • Val (from value)– invariant reference. Once a quantity declared as val is initialized, it cannot be reassigned. Corresponds to the final variable in Java
  • Var (from variable)- a mutable reference. The value of a variable can be changed. Corresponds to normal variables in Java (non-final)

In general, try to declare all variables as val keywords. Var is changed only when necessary. Use immutable references, immutable objects, and functions with no side effects to move your code closer to functional programming style.

The val variable can be initialized only once during the execution of a block of code that defines it. However, if the compiler can ensure that only one initialization statement is executed, it can initialize it with a different value depending on the condition:

val message: String
if (canPerformOperation()) {
    message = "Success"
    // ... perform the operation } 
else {
    message = "Failed" 
}
Copy the code

** Note: ** Although the val reference itself is immutable, the object it points to can be mutable:

val languages = arrayListOf("Java"// Declare an immutable reference languages.add("Kotlin"// Change the instance to which the reference pointsCopy the code

** Although the var keyword allows a variable to change its value, its type is determined:

var answer = 42 
answer = "no answer"// Compilation error: Type mismatchCopy the code

The compiler only deduces the type of a variable from the initializer, and does not consider subsequent assignments when determining the type.

If you want to store a value of a mismatched type in a variable, you must convert or covariant the value to the correct type.

1.4 Easier String formatting: String templates

Fun main(args: Array<String>) {// Print "Hello, Kotlin", if the input parameter is Bob, print "Hello, Bob" val name =if (args.size > 0) args[0] else "Kotlin" 
    println("Hello, $name!")}Copy the code

This example introduces a feature called String templates. Like other scripting languages, Kotlin allows references to local variables in string literals by prefixing the variable name with the $character. This concatenates with strings in Java (“Hello, “+ name + “!” ), but more compact and efficient (note: creating StringBuilder, adding constant parts and variable values, optimized for Java Virtual machines).

If you reference a nonexistent local variable, the code will not compile successfully because the expression will be statically checked. If you want to include it in a stringX, instead of translating x into a reference to a variable.

Instead of a simple variable name, you can use more complex expressions by enclosing the expression in curly braces:

Fun main(args: Array<String>) {// useThe ${}Insert the first element of the ARGS arrayif (args.size > 0) { println("Hello, ${args[0]}!")}}Copy the code

You can also double quote inside double quotes, as long as they are in the same expression:

fun main(args: Array<String>) { 
    println("Hello, ${if (args.size > 0) args[0] else "someone"}!")}Copy the code

Classes and attributes

Object-oriented programming may not be a new topic, and Kotlin is familiar with it, but you’ll find that many common tasks can be done with much less code.

Let’s look at a simple JavaBean Person class that now contains only one name attribute:

/* Java */ 
public class Person { 
    private final String name;
   
    public Person(String name) { 
        this.name = name;     
    }
    
    public String getName() { 
        returnname; }}Copy the code

In Java, the body of a constructor often contains duplicate content, assigning parameters to fields with the same name. In Kotlin, this logic doesn’t require so much boilerplate code.

/* Kotlin*/ 
class Person(val name: String)
Copy the code

Such classes (only data and no other code) are often called value objects

Note that the public modifier is missing in the conversion from Java to Kotlin. Public is the default visibility in Kotlin.

2.1 attributes

  • In Java, if you want a user of a class to access data, you need to provide access methods: a getter, possibly a setter, which may contain some additional logic, validate passing a value, or send notification of a value change, and so on.
  • But in Koltin, attributes are first-class language features, completely replacing field and visitor methods. Use the val and var keywords. Declare that val’s property is read-only and var is mutable
Class Person(val name: String, // Read-only attributes: automatically generate a domain and simple getters)Copy the code

Let’s look at how to use the Person class defined above:

val person = Person("Bob".true)
println(person.name)// Bob
println(person.isMarried) //true
Copy the code

You can now reference properties directly, no getters are needed, the logic is the same, the code is much cleaner.

Tip:

  • You can use Kotlin’s attribute syntax in java-defined classes. Getters in Java classes can be obtained from the val property in Kotlin, and getters/setters can be obtained from the var property. For example, if the setName and setName methods are defined in a Java class, they can be obtained through an attribute called name. If the class defines isMarried and setMarried methods, the corresponding Kotlin property is called isMarried.

2.2 Custom property accessors

In this section, you will see how to implement a custom property accessor. If you declare a rectangle, it can tell you if it’s a square. You don’t need to store this information in a separate field, because you need to dynamically check if the height equals the width:

class Rectangle(val height: Int, val width: Int) { 
    val isSquare: Boolean 
    get() { //Property getter declaration
        return height == width
    } 
}
Copy the code

The isSquere property does not require a field to store its value. It is simply a getter for your custom implementation.

val rectangle = Rectangle(41, 43)
println(rectangle.isSquare) //false
Copy the code

2.3 Kotlin source layout: directories and packages

Java puts all classes in a package. Kotlin, like Java, has the concept of a package. Each Kotlin file has a package statement at the beginning, under which all the declarations (classes, functions, and attributes) in the file will be placed. If other files are in the same package, all definitions can be used directly. If these definitions are in different packages, they need to be imported. As in Java, import statements are placed at the beginning of the file, using import keywords. Here is an example of a package declaration and import statement:

Rectangle(Val height: Int, val width: Rectangle); Rectangle(val height: Int, val width: Rectangle); Int) { val isSquare: Boolean get() = height == width } fun createRandomRectangle(): Rectangle { val random = Random()return Rectangle(random.nextInt(), random.nextInt()) 
}

Copy the code

Let’s take a look at the directory structures for Java and Kotlin

In Kotlin, you can put multiple classes in the same file, and you can choose the name of the file.

In most cases, however, it is best practice to follow the Java directory structure and organize the source into directories based on the package structure. Especially for Kotlin/Java hybrid projects, it’s important to stick to this structure. Because doing so allows you to migrate your code incrementally, without introducing unexpected situations. But when the class is small (in Kotlin, these are often present). Do not hesitate to combine multiple classes into the same file.

conclusion

  • The fun keyword is used to declare functions. The val keyword and the var keyword are used to declare read-only and mutable variables, respectively
  • String templates help you avoid tedious string concatenation. Prefixes the string{} surrounds an expression to inject a value into a string.
  • Entity classes (value object classes) are represented more simply in Kotlin.
  • In Kotlin, you can put multiple classes in the same file, and you can choose the name of the file.