Kotlin基础:面试中的问题

109 阅读4分钟

为什么需要这样写this@MainActivity

this指当前对象。this@label是指this来源的标签label。

class MainActivity() : Activity() {
    private fun <T> Intent.openActivity(block: Intent.() -> Unit, clz: Class<T>) {
        // MainActivity的this
        val intent = Intent(this@MainActivity, clz)
        // Intent的this
        this.putExtra("name", "open")
        intent.block()
        startActivity(intent)
    }
}

Kotlin函数和Lambda之间的关系

Lambda是函数类型的实例。Kotlin中定义一个函数事实上是在Java中定义一个FunctionN类型的接口

val block:(()->Unit)?=null
反编译成java
private static final Function0 block;

Kotlin安全类型转换

类型转换使用as

fun getMainActivity() = activity as MainActivity
java写法:
protected MainActivity getMainActivity(){
    return (MainActivity)getActivity();
}

常规的类型转化可能报ClassCastException,Kotlin中由更加安全的写法as?

fun getMainActivity() = activity as? MainActivity

as?尝试转换目标类,转换失败则返回null。反编译成java

public final MainActivity getMainActivity(){
    FragmentActivity activity = this.getActivity();
    if(!(activity instanceof MainActivity)){
        activity = null;
    }
    return (MainActivity)activity;
}

Kotlin安全判空

fun main() {
    var student:Student?=null
    info(student)
}
fun info(student:Student?){
    student?.name
}
class Student(var name:String)

在java中的实现

public final class FunKt{
    public static final void main(){
        Student student =null;
        info((Student)student);
    }
    public static void main(String[] var0){main()}
    public static final void info(@Nullable Student student){
        if(student!=null){
            student.getName();
        }
    }
}

?.使用了if(student!=null)判断逻辑。?.操作符非空就正常返回,如果是空则返回null。

student?.name?.hashCode()

Kotlin中一个类的无参构造函数,什么时候可以省略constructor关键字

如果在没有注解或可见性修饰符,则可以省略constructor关键字

// 可以省略
class Car{}
// 不可以省略
class Car private constructor(){}
// 不可以省略
class Car @Inject constructor(){}
// 不可以省略
class Car @Inject private constructor(){}

具体化参数类型

是Kotlin结合泛型和内联函数的概念提供的语法糖。在编译期间,将参数类型转化成具体的类型,在运行时不需要考虑泛型擦除机制。在运行时不存在泛型了,就是一个具体的类型。具体化的参数类型,让obj as Tobj is T可以在实际开发中运用

fun main() {
    printInfo<BaseFragment>(MainFragment())
}
inline fun <reified T> printInfo(obj:T){
    println(T::class.simpleName)
    if (obj is BaseFragment){
        println("obj is BaseFragment")
    }
    println("${obj as BaseFragment}")
}
class MainFragment:BaseFragment()
open class BaseFragment

定义一个内联泛型函数printInfo,使用reified关键字修饰泛型T。具体化的参数类型必须在内联函数中声明,在printInfo()方法内部打印了该类型的参数。具体可以反编译java看具体实现。

Kotlin万物皆对象

Java中不是万物皆对象,Java中存在基本数据类型时一种值类型,值类型通常被分配到栈上,它的变量直接包含变量的实例。值类型和基本数据类型包装类之间的相互转换关系,称为装箱和拆箱。所以说Java中基本数据类型并不是对象,而基本数据类型包装类是对象。严谨说,java并不是万物皆对象。

可变参数

在Kotlin中使用vararg关键字来声明,java中又称可变数组。

fun main() {
    printInfo("1","2","3")
}
private fun printInfo(vararg array:String){
    for (item in array){
        println(item)
    }
}

java 中的实现
private void test(){
    printInfo("1","2","3");
}
private static final void printInfo(String... array){
    String[] var = array;
}

Kotlin中使用vararg修饰。java中用ClassName...修饰。

Kotlin中属性,自定义getter和setter访问器

对于final只读的val类型函数,可以自定义get(),但并不是所有的只读属性需要自定义get(),如果该属性没有根据一个动态条件实现的改变,没必要自定义get()。

// 可以不用自定义
val name get() = "test"
// 需要自定义
val name get()=if(...) name = else name=

对于JVM平台,通过默认getter和settter访问私有化属性会被优化,所以不会引入函数调用开销。 如果对于默认就是无法改变的只读属性,完全没有必要自定义getter访问器。对于可变的var属性来说,getter和setter访问器是可以自定义的,因该是按需实现。

apply函数实现:

// apply是顶层函数
// 可以使用拖尾Lambda表达式,apply函数拥有一个函数类型的参数
// 可以在任意对象上调用,apply函数是一个扩展函数
// 拥有一个调用者的返回值
// 拥有当前上下文的作用域
public inline fun <T> T.apply(block: T.() -> Unit):T{
    block();
    return this;
}

总结

kotlin在Android开发中是十分重要的,需要我们更加深刻的理解其中的语法含义,以及使用场景。