Your support means a lot to me!

🔥 Hi, I’m Xu Rui. This article has been included in GitHub · Android-Notebook. Here are Android advanced growth route notes & blog, like-minded friends, welcome to grow with me. (Contact & Group entry on GitHub)

preface

  • In the Android interview, we attach great importance to the investigation of basic knowledge, which mainly includes Java, Kotlin, C/C++ three programming languages. In xiaopeng’s interview experience, I found that many students’ Kotlin language ability only stops at some very basic grammar use;
  • In this article, I will summarize the most common points and principles of Kotlin for you. Hopefully this article will help you clear up support blind spots and get a feel for some of the principles behind syntax.

1. Why Kotlin?

The interviewer may ask this question in order to introduce the topic of Kotlin first, and in order to test your cognitive ability, whether you have really thought about Kotlin’s strengths/values, or whether you are just going along with the crowd. You can reply by saying:

In the Android ecosystem, there are mainly C++, Java, Kotlin three languages, their relationship is not replacement but complementary. While the C++ context is algorithmic and high performance, the Java context is platform independent and memory management, Kotlin combines the best features of many languages to bring a more modern approach to programming. Examples include coroutines that simplify asynchronous programming, nullability that improves code quality, lambda expressions, etc.

2. The taste of sugar

  • == is the same as equal(), === compares memory addresses

  • The essence of a Kotlin top-level member is a Java static member. When compiled, classes with the fileName Kt are automatically generated. You can use the @JVM :fileName annotation to change the automatically generated class name.

  • How the default parameters work: So you can’t call a function with a default parameter in Java. You need to add @jvmoverloads to Kotlin. Instructs the compiler to generate overloaded methods (@jvMoverloads provide overloaded methods for default parameters).

  • How deconstruction declarations work: Kotlin deconstruction declarations can decompose an object’s attributes into a set of variables, so the nature of a deconstruction declaration is local variables.

    For example: Val (name, price) = Book("Kotlin ", 66.6 f) println (name) println (price) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Kotlin class need to declare ` operator fun Class Book(var name: String, var price: Float) {Operator Fun Component1 (): String {return name} operator fun component2(): Float {return price}}Copy the code
  • The key to improving performance for Sequences is that multiple operations share the same Iterator and can complete data operations in a single loop. Sequences are lazy and need terminal operations to start working.

  • How extension functions work: The semantics of extension functions are to add new functions or attributes to a class without modifying/inheriting the class. Static functions in nature, which take the receiver type as their first argument, call the extension without creating an adaptation object or any run-time overhead. In Java, we just need to call extensions like normal static methods. Related: Kotlin | extension functions (finally know why with this, let use it)

  • Let, apply, and Apply are all standard library functions. The main difference is that the lambda parameter types are defined differently. The lambda arguments of apply and with are extension functions of T, so use this to refer to the receiver object in lambda, while the lambda arguments of let are higher-order functions with arguments of T, so use IT to refer to the only argument in lambda.

  • How the delegate mechanism works: The Kotlin delegate’s syntactic keyword is BY, which is essentially a compiler-oriented syntactic sugar. All three kinds of delegates (class delegates, object delegates, and local variable delegates) are converted to “sugar-free syntax” at compile time. For example, class delegation: The compiler implements all the methods of the underlying interface and delegates them directly to the underlying object. Examples are object delegates and local variable delegates: auxiliary properties (prop$degelate) are generated at compile time, while getter() and setter() methods for properties/variables are simply delegated to the getValue() and setValue() handling of auxiliary properties. Related: Kotlin | entrusted mechanism & principle & application

  • Infix function: The function that declares the infix keyword is an infix function. The infix function can be called by omitting the dot and parentheses to make the statement more natural.

    Infix function requirements: -1, member function or extension function - 2, function has only one argument - 3, cannot use variable arguments or default arguments example: infix Fun String. Fruit: String {return "${this} eat ${fruit}"}Copy the code

3. Type system

  • Value types: Kotlin unifies the basic data types and reference types as Byte, Short, Int, Long, Float, Double, Char, and Boolean. Note that the unification of types does not mean that all of Kotlin’s numeric types are reference types. In most cases, they become primitive data types when compiled, and type parameters are compiled as reference types.

  • Implicit conversions: Kotlin does not have implicit conversions, which require explicit conversions to high-level types even if they are low-level:

    Val ddLong: Long = anint.tolong () val ddLong: Long = anint.tolong ()Copy the code
  • Platform type: When nullability annotations do not exist, Java types are converted to Kotlin’s platform type. Platform types are essentially nullable information that the Kotlin compiler cannot determine, and can be treated as either nullable or non-nullable.

    It would be unreasonable to treat all values from Java as non-null, whereas treating Java values as nullable would lead to a lot of Null checks. All things considered, the platform type is Kotlin’s design compromise for developers.

  • Type conversion: A smaller type is not a subtype of a larger type, and a smaller type cannot be implicitly converted to a larger type.

    Val b: Byte = 1 // OK val I: Int = b // compiler error val I: Int = b.toint () // OKCopy the code
  • Read-only sets and mutable sets: read-only sets are only readable, while mutable sets can add or remove this difference (for example, List is read-only, MutableList is mutable). Note that the set to which a read-only collection reference refers is not necessarily immutable, because the variable you use may be one of many that refer to the same collection.

  • Array is an Array of the reference type Integer[], and IntArray is an Array of the numeric type int[].

  • Unit: a subclass of Any, which can be omitted as a return value of a function.

  • Nothing: expression or function never returns, Nothing? The only value allowed is null.

  • Java Void: a wrapper for Void, similar to Void, to indicate that a function has no valid return value. The return value must be null.


4. Object oriented

  • Class modifiers: Kotlin classes/methods are final by default. If you want to inherit classes/override methods, you need to add an open modifier to the base class/method.

    Final: not allowed to inherit or overwrite open: allowed to inherit or overwrite abstract: abstract class/methodCopy the code
  • Access modifiers: Java’s default access modifier is protected and Kotlin’s default access modifier is public.

    A module is a set of compiled Kotlin files. Protected: visible in subclasses (unlike Java, the same package is not visible; Kotlin is not visible as the default package) Private: visible in classesCopy the code
  • Constructor:

    • Default constructor: class has a primary constructor with no arguments by default. If the constructor is explicitly declared, the default primary constructor with no arguments is invalid.
    • Main constructor: declared after the class keyword, where the constructor keyword can be omitted;
    • Secondary constructors: If a secondary constructor is declared, the default primary constructor with no arguments is invalidated. If a primary constructor exists, the secondary constructor needs to delegate directly or indirectly to the primary constructor.
  • Init functions are executed in order: primary constructor > init > secondary constructor

  • Inner class: Kotlin defaults to a static inner class. If you want to access member methods and attributes in the class, you need to add the inner keyword to call it a non-static inner class. Java defaults to non-static inner classes.

  • The data keyword is used to define data types, and the compiler automatically extracts properties from the main constructor and generates a set of functions: equals()/hashCode(), toString(), componentN(), copy().

  • Sealed Keyword principle: Sealed class is used to represent a restricted class inheritance structure. A sealed class can have subclasses, but all subclasses must be embedded within the sealed class.

  • A Companion object is a companion object, which can have only one member (function/attribute) of the class. The companion object is a companion object.

  • Singletons: Kotlin can implement singletons using Java-like methods or using kotlin-specific syntax. Related information: Five singleton patterns under Kotlin

    • object
    // Kotlin implements object SingletonDemoCopy the code
    • by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED)
    class SingletonDemo private constructor() {
        companion object {
            val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
    		        SingletonDemo() 
    				}
        }
    }
    Copy the code
  • Generics: Here’s everything you can ask about generics (including Kotlin)


Lambda expressions

  • Lambda expressions are essentially “blocks of code that can be passed as values.” In older Versions of Java, passing code blocks was implemented using anonymous inner classes, whereas lambda expressions don’t even require function declarations and can pass code blocks directly as function values.

  • It: When a lambda expression has only one argument, the it keyword can be used to refer to a unique argument.

  • The type of lambda expression

    • 1. Ordinary Lambda expressions: for example ()->R
    • Lambda expressions with receiver objects: for example, T.()->R
  • How lambda expressions access local variables: In Java, local variables accessed by anonymous inner classes must be final; otherwise, they need to be wrapped in arrays or objects. In Kotlin, lambda expressions can directly access non-final local variables by providing a layer of wrapper classes, and modifying local variables is essentially modifying properties in the wrapper class.

    class Ref<T>(var value:T)
    Copy the code
  • Lambda expression compiler optimization: When using a lambda expression in Java 8 and Kotlin in a loop, there is compile-time optimization, where the compiler optimizes the lambda to be a static variable unless an external variable or function is accessed in the lambda expression.

  • Inline functions work as follows:

    • Inline lambda expression arguments (main advantage) : Arguments to an inline function that are lambda expressions are also inline by default. Lambda expressions are also solidified into function call locations, thus reducing the overhead of creating anonymous inner class objects for lambda expressions. When lambda expressions are called frequently, memory overhead can be reduced.

    • Reduced loading and unloading (secondary advantage) : The function body of the inline function is solidified to the function call location, eliminating stack frame creation, loading and unloading during execution. Note: If the function body is too large, it is not suitable to use inline functions, because the bytecode size will be greatly increased.

    • @publishapi annotation: The compiler requires that inline functions be of public type. Using the @publishapi annotation can implement access modifications such as internal while also inlining

    • Noinline non-inline: If a lambda expression argument is called by another non-inline function inside an inline function, a compile-time error is reported. This is because lambda expressions have been flattened and cannot be passed to other non-inline functions. A parameter can be given the noinline keyword to disable inlining.

      inline fun test(noinline inlined: () -> Unit) {
          otherNoinlineMethod(inlined)
      }
      Copy the code
    • Non-local returns: An unlabeled return statement can only be used in functions declared by fun, so returns in lambda expressions must be labeled to indicate which level of function to return:

      Fun song(f: (String) -> Unit) {// do something} fun behavior() {song {println("song $it") return 'return' is not allowed here return@song // partial return return@behavior // non-partial return}}Copy the code

      The only exception is lambda expression arguments in inline functions. You can use an unlabeled return that returns the external function calling the inline function, rather than the inline function itself. By default, the return is nonlocal.

      inline fun song(f: (String) -> Unit) {// do something} fun behavior() {song {println("song $it") return return@song // Local return return@behavior // Non-local return}}Copy the code
    • Crossinline nonlocal Return: Disallows nonlocal returns for lambda expression arguments of inline functions

    • Reified: Because of generic erasure, the type of the type argument is not known at runtime. Kotlin can overcome this limitation by using an inline function with a validated type parameter. The actual type parameter is replaced by the exact type of the type argument when inserted into the call location, so the type of the argument can be determined.

      Java: <T> List<T> filter(List List) {List<T> result = new ArrayList<>(); Java: <T> List > filter(List List) {List<T> result = new ArrayList<>(); for (Object e : list) { if (e instanceof T) { // compiler error result.add(e); } } return result; } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Kotlin: fun < T > filter (list: a list < * >) : List<T> { val result = ArrayList<T>() for (e in list) { if (e is T) { // cannot check for instance of erased type: T result.add(e)}} return result}  val list = listOf("", 1, False) val strList = filter < String > (list) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- after the inline:  val result = ArrayList<String>() for (e in list) { if (e is String) { result.add(e) } }Copy the code

5. DSL Domain specific language

DSLS are a language designed to solve a particular problem. While not as comprehensive as a general-purpose language, they are more efficient at solving a particular problem. Case in point: Compose’s UI code also uses DSL, giving Compose the same coding efficiency as XML. Kotlin DSL: Composing code like Compose

  • Higher-order functions: take the lambda argument out of parentheses and subtract one argument;

  • Extension functions: pass Receiver, reducing one argument;

  • Context Receivers: Pass multiple Receivers, reducing multiple parameters from the extension function.

  • Infix function: make syntax more concise and natural;

  • @dslMarker: Used to restrict untagged this in a lambda to accessing only the nearest Receiver type. this@XXX must be explicitly specified when calling an outer Receiver.

    context(View)
    val Float.dp 
        get() = this * [email protected]
    
    class SomeView : View {
      val someDimension = 4f.dp
    }
    Copy the code

6. Summary

A few smart people might ask, “Why don’t you mention coroutines and Flow?” That is because these knowledge points are more, xiao Peng decided to put in a separate article. Doesn’t it smell good when an article is split into two?

Your likes mean a lot to me! Wechat search public number [Peng Xurui], I hope we can discuss technology together, find like-minded friends, we will see you next time!