官术网_书友最值得收藏!

Type inference

As we saw in previous examples, unlike Java, the Kotlin type is defined after the variable name:

    var title: String 

At first glance, this may look strange to Java developers, but this construct is a building block of a very important feature of Kotlin called type inference. Type inference means that the compiler can infer type from context (the value of an expression assigned to a variable). When variable declaration and initialization is performed together (single line), we can omit the type declaration. Let's look at the following variable definition:

    var title: String = "Kotlin" 

The type of the title variable is String, but do we really need an implicit type declaration to determine variable type? On the right side of the expression, we have a string, Kotlin and we are assigning it to a variable, title, defined on the left-hand side of the expression.

We specified a variable type as String, but it was obvious, because this is the same type as the type of assigned expression (Kotlin). Fortunately, this fact is also obvious for the Kotlin compiler, so we can omit type when declaring a variable, because the compiler will try to determine the best type for the variable from the current context:

    var title = "Kotlin" 

Keep in mind that type declaration is omitted, but the type of variable is still implicitly set to String, because Kotlin is a strongly typed language. That's why both of the preceding declarations are the same, and Kotlin compiler will still be able to properly validate all future usages of the variable. Here is an example:

    var title = "Kotlin" 
    title = 12 // 1, Error 
  1. The inferred type was String and we are trying to assign Int.

If we want to assign Int (value 12) to the title variable then we need to specify title type to one that is a String and Int common type. The closest one, up in the type hierarchy, is Any:

    var title: Any = "Kotlin" 
    title = 12
The Any type is an equivalent of the Java object type. It is the root of the Kotlin type hierarchy. All classes in Kotlin explicitly inherit from type Any, even primitive types such as String or Int.
Any defines three methods: equals, toString, and hashCode. The Kotlin standard library contains a few extensions for this type. We will discuss extensions in Chapter 7, Extension Functions and Properties.

As we can see, type inference is not limited to primitive values. Let's look at inferring types directly from functions:

    var total = sum(10, 20) 

In the preceding example, the inferred type will be the same as the type returned by the function. We may guess that it will be Int, but it may also be a Double, Float, or some other type. If it's not obvious from the context what type will be inferred, we can use place caret on the variable name and run the Android Studio expression type command (for Windows, it is Shift + Ctrl + P, and for macOS, it is arrow key + control + P). This will display the variable type in the tooltip, as follows:

Type inference works also for generic types:

    var persons = listOf(personInstance1, personInstance2) 
// Inferred type: List<Person> ()

Assuming that we pass only instances of the Person class, the inferred type will be List<Person>. The listOf method is a helper function defined in the Kotlin standard library that allow us to create collections. We will discuss this subject in Chapter 7, Extension Functions and Properties. Let's look at more advanced examples that uses the Kotlin standard library type called Pair, which contains a pair composed of two values:

    var pair = "Everest" to 8848 // Inferred type: Pair<String, Int> 

In the preceding example, a pair instance is created using the infix function, which will be discussed in Chapter 4, Classes and Objects, but for now all we need to know is that those two declarations return the same type of the Pair object:

    var pair = "Everest" to 8848   
// Create pair using to infix method var pair2 = Pair("Everest", 8848)
// Create Pair using constructor

Type inference works also for more complex scenarios such as inferring type from an inferred type. Let's use the Kotlin standard library's mapOf function and infix the to method of the Pair class to define map. The first item in the pair will be used to infer the map key type; the second will be used to infer the value type:

    var map = mapOf("Mount Everest" to 8848, "K2" to 4017) 
    // Inferred type: Map<String, Int> 

Generic type of Map<String, Int> is inferred from type of Pair<String, Int>, which is inferred from type of parameters passed to the Pair constructor. We may wonder what happens if inferred type of pairs used to create map differs? The first pair is Pair<String, Int> and second is Pair<String, String>:

    var map = mapOf("Mount Everest" to 8848, "K2" to "4017") 
    // Inferred type: Map<String, Any> 

In the preceding scenario, the Kotlin compiler will try to infer a common type for all pairs. The first parameter in both pairs is String (Mount Everest, K2), so naturally String will be inferred here. The second parameter of each pair differs (Int for the first pair, String for the second pair), so Kotlin needs to find the closest common type. The Any type is chosen, because this is the closest common type in the upstream type hierarchy:

As we can see, type inference does a great job in most cases, but we can still choose to explicitly define a data type if we want, for example, if we want different variable types:

    var age: Int = 18

When dealing with integers, the Int type is always the default choice, but we can still explicitly define different types, for example, Short, to save some precious Android memory:

    var age: Short = 18

On the other hand, if we need to store larger values, we can define the type of the age variable as Long. We can use explicit type declaration as previously, or use a literal constant:

    var age: Long = 18 // Explicitly define variable type
    var age = 18L      
// Use literal constant to specify value type

Those two declarations are equal, and both of them will create a variable of type Long.

For now, we know that there are more cases in code where the type declaration can be omitted to make code syntax more concise. There are, however, some situations where the Kotlin compiler will not be able to infer type due to a lack of information in context. For example, a simple declaration without assignment will make type inference impossible:

    val title // Error 

In the preceding example, the variable will be initialized later, so there is no way to determine its type. That's why type must be explicitly specified. The general rule is that if the type of expression is known for the compiler, then type can be inferred. Otherwise, it must be explicitly specified. Kotlin plugin in Android Studio does a great job because it knows exactly where type cannot be inferred and then it is highlighted as an error. This allows us to display proper error messages instantly in the IDE when writing the code, without the need to complete the application.

主站蜘蛛池模板: 宝清县| 荥阳市| 榆社县| 格尔木市| 株洲县| 离岛区| 德化县| 大新县| 贵德县| 盘锦市| 洞头县| 偃师市| 屏山县| 盈江县| 莎车县| 关岭| 巍山| 永春县| 北海市| 九龙县| 哈密市| 邻水| 榕江县| 石渠县| 唐河县| 长宁区| 清流县| 溧水县| 昭通市| 株洲县| 调兵山市| 健康| 阜宁县| 华宁县| 霞浦县| 盈江县| 长岭县| 桑日县| 杭锦后旗| 兴文县| 秦皇岛市|