美文网首页程序员
通过面试题学习Kotlin的知识点(1)

通过面试题学习Kotlin的知识点(1)

作者: 7mile | 来源:发表于2020-06-12 09:42 被阅读0次

    1、如果定义一个int数组,下面哪个正确:

    val arrs = IntArray(5)
    val arrs = Array<Int>(5)
    val arrs : Int[5]
    

    2、哪种方法可以合并下面两个数组a和b到一个list:

    val a = arrayOf(1, 2, 3)
    val b = arrayOf(100, 200, 300)
    val c = a + b
    val d = listOf(a + b)
    val e = listOf(a, b)
    val f = listOf(*a, *b)
    

    3、init blocks和properties在初始化时的执行顺序是哪个:

    • 所有的properties按照出现的顺序初始化,然后是init blocks
    • 所有的init blocks按照出现的顺序初始化,然后是properties
    • init blocks 和 properties按照出现的顺序初始化

    4、const和@JvmField都可以创建常量,拿一下是@JvmField可以做但是const做不了的:

    class Styles {
      companion object {
        const val COLOR = "Blue"
        @JvmField val SIZE = "Really big"
    }
    
    • @JvmField总是被inline使得代码执行更快
    • @JvmField跟Java兼容,const不行
    • @JvmField可以作为顶层变量,但是const只能在类中使用
    • const只能标记原始变量和String,@JvmField没有这种限制

    5、Kotlin的接口和抽象类非常像,哪一件事是抽象类可以做但是接口做不了的:

    • 只有抽象类可以有抽象方法
    • 只有抽象类可以被子类继承
    • 只有抽象类可以存储状态
    • 只有抽象类可以从多个父类继承

    6、fun foo(obj : Any), 怎么获得obj的原始类型,包括package信息:

    a. obj.classInfo()
    b. obj::class
    c. obj.typeInfo()
    d. obj::class.simpleName
    

    7、定义一个带有name属性的类,要求延迟初始化,且不能为null。如果要用delegate来做,一个怎么声明:

    a. var name: String by Delegates.nonNull()
    b. var name: String? = null
    c. lateinit var name: String
    d. var name: String by lazy
    

    答案

    1、第1个是正确的,第2个没有初始化,所以编译失败,需要写成Array<Int>(5) {i} 这样的形式。

    Kotlin有一些预先定义的Array类,比如IntArray, LongArray, ByteArray, DoubleArray, 他们都有一个只需一个size参数的constructor,会转换成java的对应类型的数组,比如int[], long[], byte[], double[]。他们的初始值都是对应类型的0值,比如int,long,short都是0,而char的0值是‘\u0000’。

    如果要建一个int/long的数组,用IntArray/LongArray是比较方便的,因为初始化的init块是optional的。
    上面的数组都不能接受null,如果要传入null,可以使用如下方法:

    val arr = arrayOfNulls<Int>(5)
    arrayOfNulls方法定义如下:
    /**
     * Returns an array of objects of the given type with the given [size], initialized with null values.
     */
    public fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
    

    生成的是Array<Int?>类型

    2、正确的是 listOf(*a, *b),把每个数组展开,然后生成一个list

    a + b: [Ljava.lang.Integer;@4517d9a3     ->  a+b是一个包含各个元素的新数组
    listOf(a + b): [[Ljava.lang.Integer;@372f7a8d]   -> 生成的是只有一个数组元素的列表
    listOf(*a, *b): [1, 2, 3, 100, 200, 300]      ->  正确
    listOf(a, b): [[Ljava.lang.Integer;@279f2327, [Ljava.lang.Integer;@2ff4acd0] -> 生成的是有两个数组元素的列表
    

    之所以可以用+把两个array放到一个数组里,是因为Array类有一个扩展方法:

    /**
     * Returns an array containing all elements of the original array and then all elements of the given [elements] array.
     */
    public actual operator fun <T> Array<T>.plus(elements: Array<out T>): Array<T> {
        val thisSize = size
        val arraySize = elements.size
        val result = java.util.Arrays.copyOf(this, thisSize + arraySize)
        System.arraycopy(elements, 0, result, thisSize, arraySize)
        return result
    }
    

    3、正确答案是 init blocks 和 properties按照出现的顺序初始化

    class Test{
        private val a = 1.also { println("init a") }
        init {
            println("init 1")
        }
    
        private val b = Long.MAX_VALUE / 0
    
        init {
            println("init 2")
        }
    }
    ---output---
    init a
    init 1
    Exception in thread "main" java.lang.ArithmeticException: / by zero
    

    可以看到"init 2"没有输出,"init 1"在抛出异常前输出了。

    4、第4个正确,const只能修饰Int/Long等原始类和String,其他的需要用@JVMField修饰。const修饰的常量可以被inline,效率更高,而@JVMField修饰的常量会被转化为public final static的常量,跟Java定义的类似。

    我们在开发中一般会把常量定义在一个companion object里,但是编译之后也会自动生成一个单例类实例,所以如果companion object中只有一些常量定义,还是把这些常量放到顶层,不放到任意类中。比如:

    Constants.kt类:
    const val MAX = 100
    const val TYPE = "PAYMENT"
    class Foo { }
    

    推荐阅读:where-do-i-put-my-constants-in-kotlin

    5、第3个正确,只有抽象类可以存储状态。
    一个类只能继承一个抽象类,但是可以实现多个接口,所以选项4错误。抽象类和接口都可以有抽象方法。

    抽象类可以定义状态:

    abstract class Animal {
        var name: String = "Default name"
        var type: String = "Default type"
    
        val fullName: String
            get() = "$name of type $type"
    }
    
    class Lion : Animal() 
    

    Interfaces 不能有fields,但是可以有properties

    interface Animal {
        val name: String
    }
    --- decompile成Java:---
    public interface Animal {
       @NotNull
       String getName();
    }
    

    可以看到只有一个getter方法,所以Animal接口里不能对name指定初始值

    推荐阅读:abstract-class-vs-interface-in-kotlin

    6、obj::class正确,可以自己试试,考察对获取类信息方法的熟悉度。
    下面是Any类的定义:

    public open class Any {
        public open operator fun equals(other: Any?): Boolean  // 正是因为有了这个operator,才可以用==来判断两个对象是否内容相等
        public open fun hashCode(): Int
        public open fun toString(): String
    }
    

    Any类跟java的Object类很像,但是方法却少了几个。可以看到并没有classInfo()和typeInfo()方法。

    7、c选项[lateinit var name: String]是正确的。
    d选项,name必须是val不变的,而且lazy后需要加一个初始化代码块:

    class Lion : Animal {
        var name: String by lazy {
            "Lion"
        }
    }
    ---编译失败---
    Type 'Lazy<String>' has no method 'setValue(Lion, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property)
    

    调用lazy方法其实是生成了一个Lazy<String>的实例返回代替name属性,由于Lazy没有setValue的方法,所以不能用于修饰var变量。

    相关文章

      网友评论

        本文标题:通过面试题学习Kotlin的知识点(1)

        本文链接:https://www.haomeiwen.com/subject/stqbtktx.html