Kotlin与Java比较:函数

2018-12-08  本文已影响2人  程序引力

前言

Kotlin作为JVM系的语言,起源于Java又不同于Java。通过在语言层面比较两者的区别,可以使得开发者能够快速学习,融会贯通。

函数声明

[访问控制符] 返回值类型 方法名([参数列表]){
    方法体
}

即返回值类型,方法名,方法体必不可少,这样的形式组合在一起就是函数声明。如:

public int plus(int x, int y){
    return x + y;
}
fun 方法名([参数列表]) [:返回值类型][方法体]

简单的例子:

fun double(x: Int): Int {
    return 2 * x
}

函数参数

在程序语言中,函数参数可以分为两类,分别是位置参数与命名参数。

对于位置参数,即一个函数有多个参数时,它通过位置来判断哪一个值对应哪一个参数。Java函数仅支持位置参数。

对于命名参数,即通过参数的名字来判断哪一个参数值与参数对应。Kotlin既支持单独使用任意一种参数传递方式,也支持同时使用两种方式。

public int minus(int x, int y){
    return x - y;
}

// 调用时
minus(3,1);

即当调用minus(3,1)方法时,第一个参数值3对应第一个参数x,第二个参数值1对应第二个参数y。

fun minus(x: Int, y: Int): Int {
    return x - y
}

minus(3,1)

使用命名参数的例子:

minus(x=3,y=1) //结果为2
minus(y=1,x=3) //结果为2

由于使用了命名参数,所以位置可以任意调整。

默认参数

在Java中不支持默认参数,但在Kotlin中支持默认参数,这带来的好处就是可以书写应用更为广泛的方法,同时避免了使用重载,减少了代码量。

public float getGravity(float m, float g){
    return m * g;
}

public float getGravity(float m){
    float g = 9.8;
    return getGravity(m,g);
}

从上面的例子可以看到,第一个方法是传入物体质量与重力常量,来计算物体所受的重力。第二个方法重载了第一个方法,只传入了质量m,但在其中使用了重力常量g的默认值9.8。

fun getGravity(m:Float, g:Float = 9.8){
    return m * g
}

通过该例子可以看到,使用默认参数非常简单,只需要在参数列表中添加‘=赋值表达式’即可。

对于继承的情况,覆盖方法总是使用与基类型方法相同的默认参数值。 当覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值:

open class A {
    open fun foo(i: Int = 10) { …… }
}

class B : A() {
    override fun foo(i: Int) { …… }  // 不能有默认值
}

如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用命名参数调用该函数来使用:

fun foo(bar: Int = 0, baz: Int) { …… }

foo(baz = 1) // 使用默认值 bar = 0

当一个函数调用混用位置参数与命名参数时,所有位置参数都要放在第一个命名参数之前。例如,允许调用 f(1, y = 2) 但不允许 f(x = 1, 2)。

函数无返回值

public void fun(){...}
fun printHello(name: String?): Unit {
    // `return Unit` 或者 `return` 是可选的
}

返回类型Unit声明也是可选的。上面的代码等同于:

fun printHello(name: String?) { …… }

单表达式函数

fun double(x: Int): Int = x * 2

当返回值类型可由编译器推断时,显式声明返回类型是可选的

fun double(x: Int) = x * 2

具有块代码体的函数必须始终显式指定返回类型,因为块代码体中逻辑较为复杂,对于编译器或者开发者来说都不易去做类型推断。只有在块代码体需要返回Unit类型时,才无需显示指定返回类型。

可变数量参数

可变数量参数基础

可变数量参数即表示函数接受的参数数量是可以变化的。

public static void dealArray(int... intArray){  
    for (int i : intArray){xxx}
}     

调用该函数时,可以有如下多种情形:

dealArray();  
dealArray(1);  
dealArray(1, 2, 3);  

即可以不传递参数,也可以传递1个或多个参数。

fun <T> asList(vararg ts: T): List<T> {
    for (t in ts){...}
}
// 使用时
val list = asList(1, 2, 3)

可变数量参数与数组

public static void dealArray(int... intArray){  //参数是可变数量的
    for (int i : intArray){xxx}
}     
// 使用时
int[] intArray = {1, 2, 3};        
dealArray(intArray);  //通过编译,正常运行  

但是对于数组参数的函数,是不兼容可变参数的,例如:

public static void dealArray(int[] intArray){  //参数是数组
    for (int i : intArray){xxx}
}     
// 使用时
dealArray(1,2,3);  //编译错误
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)

即在数组名之前加上*,可以作为参数传递给拥有可变数量参数的函数。

可变数量参数的位置

public static void dealArray(int... intArray, int count) //编译报错

函数作用域

顶层文件范围内定义函数,即函数可以在类的外部,与类“平级”:

class A(x: Int)

fun method(){...}

成员函数非常常见,但对于局部作用域定义函数,其例子为:

fun method() {
    fun innerMethod() {
    }
}

局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量:

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }
    dfs(graph.vertices[0])
}
上一篇下一篇

猜你喜欢

热点阅读