程序员

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

2020-06-12  本文已影响0人  7mile

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在初始化时的执行顺序是哪个:

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

class Styles {
  companion object {
    const val COLOR = "Blue"
    @JvmField val SIZE = "Really big"
}

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变量。

上一篇下一篇

猜你喜欢

热点阅读