Kotlin学习与实践 (三)fun 函数

通过例子来学习如何高效的在Kotlin中定义使用函数。

1、命名参数、默认参数、顶层函数、扩展函数

* 展示几种创建集合类的方法和扩展的简单操作集合类的方法
fun createCollection() {
    val set = hashSetOf(1, 12, 26)
    println("hashSetOf -- > ${set.javaClass}")
    val list = arrayListOf(12, 24, 66)
    println("arrayListOf -- > ${list.javaClass}")
    val map = hashMapOf(1 to "one", 2 to "two", 3 to "three")
    println("hashMapOf -- > ${map.javaClass}")

    val list1 = listOf("dsa", "ccc", "ddd")
    println("listOf -- > ${list1.javaClass}")
}

fun easyOptCollection() {
    val strings = listOf("ss", "this", "is", "string ArrayList")
    println(strings.last())

    val numbers = listOf(1, 200, 20, 30)
    println(numbers.max())
}

  Kotlin并没有采用它自己的集合类,而是采用标准的Java集合类,这样Kotlin就能与Java交互。

再看一个示例:

fun demand() {
    val list = listOf(1, 2, 25)
    println(list)
}
* 上面函数直接输入 list 是调用了集合的默认的toString方法,为了动态修改输入的样子,下面的几个函数是* 自己的扩展,再扩展中探讨如何让Kotlin 的方法更简单 更高效 更舒服

下面首先按照Java的习惯和风格定义一个自定义格式化输出集合的方法
/**
 * 通过再元素中间加分隔符,在最前面加前缀,再最后面加后缀把集合转成可输出的String
 * @param collection  集合
 * @param separator 分隔符
 * @param prefix 前缀
 * @param postfix 后缀
 */
fun <T> joinToString(collection: Collection<T>,
                     separator: String,
                     prefix: String,
                     postfix: String): String {
    val result = StringBuilder(prefix)
    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator) //第一个元素之前不用加分隔符
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}

 

* 为了提高代码的可读性* Kotlin支持  命名参数 --> 调用函数时 显式地指定参数名称  (牛叉的是:显示指定名称之后就可以打乱参数的传递顺序了)* 注意:为了避免混淆指明了一个参数的名称之后,后面的参数必须都要标明名称* 警告:使用Kotlin 调用Java函数时,不能采用命名参数
fun callExplicitly() {
    val list = listOf(1, 3, 5)
    println(joinToString(list, prefix = "{", separator = "\\", postfix = "}"))
}

 

* 为了避免像Java那样过多的重载与重复* Kotlin 支持默认参数值 --> 在声明函数的时候,指定参数的默认值,在调用的时候不传该参数时就使用默认的参数* 这样就可以避免创建很多的重载函数** Java中如果想调用指定默认参数的函数必须全部传递参数,如果想像在Kotlin中一样使用其省略参数的调用方式就需要给* Kotlin中声明的 指定默认参数的函数 添加“@JvmOverloads”注解,原理是编译器会带有"@JvmOverloads"的方法自动生成重载函数
@JvmOverloads
fun <T> joinToStringWithDefaultParams(collection: Collection<T>,
                                      separator: String = ",",
                                      prefix: String = "",
                                      postfix: String = ""): String {
    return joinToString(collection, separator = separator, prefix = prefix, postfix = postfix)
}

fun callWithDefaultParams() {
    val list = listOf(1, 3, 5)
    println(joinToStringWithDefaultParams(list))
    println(joinToStringWithDefaultParams(list, "-"))
    println(joinToStringWithDefaultParams(list, "-", "【", "】"))
    println(joinToStringWithDefaultParams(list, postfix = "!"))
}

  

在Java 中一些无法从属任何类又可能会被很多类频繁的调用的方法通常会抽取到专门的一个类中,以 public static final sss()最终会形成包含很多这种方法的工具类

* 在Kotlin中根本不需要去创建这样无意义的类。* 可以把这样的不从属于任何类的函数放到代码文件的顶层,这些放在文件顶层的函数依然是包内的成员* 如果要从外部访问它,直接导入包就可以用,不要额外包一层类名** 其实编译器会.kt 文件编译成Java类,类名为.kt文件名+Kt 例如:join.kt ---> JoinKt.class* 因此在Java中调用顶层函数也很简单直接导入编译的包含顶层文件的类就行了** 如果要改变包含Kotlin 顶层函数的文件被编译生成的类名,需要为这个文件添加 “@JvmName”的注解,将其放到文件的开头,位于包名的前面* 比如本类执行的名称“@file:JvmName("StringsFunctions")”
import com.mauiie.kotlin.chapter2fun.K4ExtendPropertyKt;import com.mauiie.kotlin.chapter2fun.StringsFunctions;
public class JavaCallTest {
    public static void main(String[] args) {
        ArrayList<String> strings = new ArrayList<>(5);
        strings.add("dsada");
        strings.add("adsa");
        strings.add("jklj");
        strings.add("dsada");
        System.out.println(StringsFunctions.joinToStringWithDefaultParams(strings));

        TopFunAndProperty.performOperation();
        TopFunAndProperty.performOperation();
        TopFunAndProperty.reportOperationCount();

        System.out.println(TopFunAndProperty.getUNIX_LINE_SEPARATOR());
        System.out.println(TopFunAndProperty.UNIX_LINE_SEPARATOR_CONSTANTS);
        System.out.println(TopFunAndProperty.getOpCount());

        System.out.println(K3ExtendFunAndPropertyKt.lastChar_("this is a"));
        /**
         * 扩展函数和扩展方法在Java中调用的时候都必须显示的调用
         */
        String testStr = "test";
        System.out.println(K4ExtendPropertyKt.getLastChar(testStr));
        System.out.println(K4ExtendPropertyKt.lastChar(testStr));

        Button button = new Button();
        System.out.println(button.getCurrentState());
        ;
    }

  

* 在使用JDK、Android的时候,有时会面临代码不能转成Kotlin的时候,Kotlin支持扩展函数让Kotlin支持不能转的代码* 理论上来说扩展函数非常简单,它就是一个类的成员函数,不过定义在类的外面。* 做法很简单:把要扩展的类或接口的名称,放到即将添加的函数前面。

* 这个类的名称叫 “接收者类型”;用来调用这个扩展函数的那个对象叫做“接收者对象”* 接收者类型 是由扩展函数定义的,接收者对象是该类型的一个实例
fun String.lastChar_(): Char = this.get(this.length - 1)
* String 是定义的接收者类型* this  是定义的接收这对象  也就是String的一个实例* 在扩展函数中,可以像其他成员函数一样访问类的其他变量和属性,就好像是在这个类自己的方法中访问他们一样。* 注意:扩展函数不能打破类的封装性。???????*      和类的成员变量不同的是,扩展函数不能访问类的私有或者受保护的成员。
* 在扩展函数中,可以像其他成员函数一样使用“this”,而且也可以像其他成员函数一样省略它
fun String.easyLastChar(): Char = get(length - 1)

fun main(args: Array<String>) {
    //定义好扩展函数之后就可以像普通的成员函数一样去使用了
    println("kotlin".lastChar_())
    println("test".easyLastChar())
}

  

*  在Kotlin中,重写成员函数是很平常的事情,但是,不能重写扩展函数。
* 但是,但是, 不能重写扩展函数* 扩展函数并不是类的一部分,它是声明在类之外的。尽管可以给基类和子类都分别定义一个同名的扩展函数,当这个函数被调用时* 它是由该变量的静态类型所决定的,而不是这个变量的运行时类型

* 注意: 如果一个类的成员函数和扩展函数有相同的签名,成员函数往往会被优先使用。* 记住: 如果添加一个和扩展函数一样名字的成员函数,那么对应类定义的消费者将会重新编译代码,这将会改变它的意义开始指向新的成员函数!
open class View {
    open fun click() = println("View clicked")
}
class Button : View() {
    override fun click() = println("Button clicked")
}

fun View.showOff() = println("View showOff")
fun Button.showOff() = println("Button showOff")

fun main(args: Array<String>) {
    val v: View = Button()
    val v2: View = View()
    val v3 = View()
    //具体调用哪个方法是由view的实际值来决定的,
    v.click()
    v2.click()
    v3.click()
    //但是, 不能重写扩展函数
    v.showOff()
    v2.showOff()
    v3.showOff()

    val button = Button()
    button.showOff()
}

  执行结果:

Button clicked
View clicked
View clicked
View showOff
View showOff
View showOff
Button showOff

Kotlin中直接在文件函数叫做顶层的函数,再Kotlin中看起来不从属任何类从属于包直接导入可以使用,但是从编译之后的字节码看会自动编译xxxKT.Java,顶层函数从属于编译的文件类

@file:JvmName("TopFunAndProperty")
package com.mauiie.kotlin.chapter2fun
/**
 * 这样就声明了一个顶层属性
 */
var opCount = 0 //会生成getter和setter
/**
 * 声明一个顶层函数
 */
fun performOperation() {
    opCount++
}

fun reportOperationCount() {
    println("Operation performed $opCount times ")
}

val UNIX_LINE_SEPARATOR = "\n" //只会生成getter
const val UNIX_LINE_SEPARATOR_CONSTANTS = "\n" //相当于Java中的 public static final String ...l

/**
 * 现在我们可以将joinToString 函数写出终极状态了 ---> 作为扩展函数的工具函数
 */
fun <T> Collection<T>.joinToString(
        separator: String,
        prefix: String,
        postfix: String
): String {
    val result = StringBuilder(prefix)

    // withIndex 省去了this  this.withIndex()
    for ((index, element) in withIndex()) {
        if (index > 0) result.append(separator)
        result.append(element)
    }
    result.append(postfix)

    return result.toString()
}

  2、扩展属性

* 扩展属性提供了一种方法,用来扩展类的API,可以用类访问属性,用的是属性语法而不是函数的语法。* 尽管它被称为属性,但是他们可以没有任何状态,因为没有适合的地方来存储它,不可能给现有的Java对象实例添加额外的字段。* 但有时段语法仍然是便于使用的。

先扔出来一个扩展函数
fun String.lastChar(): Char = get(length - 1)
* 上面是为String 扩展的方法 lastChar() 现在把它转成属性试试* 可以看到扩展属性也像接收者的一个普通成员属性一样。* 这里,必须定义getter函数,因为没有支持字段,因此没有默认的getter实现。* 同理,初始化也不可以:因为没有地方可以存储初始值
val String.lastChar: Char
    get() = get(length - 1)

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char) {
        this.setCharAt(length - 1, value)
    }

fun main(args: Array<String>) {
    println("Kotlin".lastChar)

    val sb: StringBuilder = StringBuilder("Kotlin")
    sb.lastChar = ‘a‘
    println(sb.lastChar)
}

 注意 

* 如果要从Java中访问扩展属性,应该显示地调用它的getter函数  K4ExtendPropertyKt.getLastChar("Java")
 *对于定义的扩展函数,不会自动在整个项目中生效,使用的时候需要像其他函数和类一样导入 * 可以使用 as 关键字来修改导入的类或者名称   同样在Java也可以使用定义好的扩展函数 参照 java.JavaCallKotlin
package com.mauiie.kotlin.chapter2fun

//import com.mauiie.kotlin.chapter2fun.*
import com.mauiie.kotlin.chapter2fun.lastChar_ as last

val c = "Kotlin".last()

 3、可变参数、中缀表示、解构声明

* a.可变参数的关键字 vararg,可以用来声明一个函数可能有任意数量的参数* b.一个中缀表示法,当你在调用一些只有一个参数的函数时,使用它会让代码更简练* c.解构声明,用来把一个单独的组合值展开到多个变量中

首先看可变参数 vararg的使用:
fun kebiancanshu() {
    val list = listOf(1, 45, 36)   //listOf源码
    //public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
}
* 上面可以看在Kotlin使用了 vararg 代替了 Java中的... 声明了可变参数* 与Java不用的一点是如果在传入参数是已经包装在数组中的参数时,在Java中会原样传递数组,而Kotlin则要求你显示着解包数组,以便于每个数组元素能作为单独的参数来调用* 从技术角度讲这个功能被称为"展开运算符",而在使用的时候不过是在参数前面放一个 “*”
fun testVararg(args: Array<String>) {
    val list = listOf("test", * args)
    println(list)
}

fun main(args: Array<String>) {
    testVararg(listOf("ds", "dsa", "111").toTypedArray())  //toTypedArray Returns a *typed* array containing all of the elements of this collection.}

  

* 声明map的代码中的to,不是内置解构,而是一种特殊函数调用,被称为中缀调用。* 在中缀调用中,没有添加额外的分隔符,函数名称是直接放在目标对象名称参数之间的。* 1 to "one"  和 1.to("one")是等价的
fun testMap() {
    val map = mapOf(1 to "one", 2 to "two", 3.to("three"))
// 源码   public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> = if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()

    val list = listOf(1.45)
    //这也是一种解构声明
    for ((index, element) in list.withIndex()) {
        println("this $index is $element")
    }
}
* 中缀调用可以与之哟普一个参数的参数的函数一起使用,无论是普通的函数还是扩展函数。* 要允许使用中缀符号调用函数,需要使用infix修饰符来标记它。* 下面是一个简单的to函数的声明

* Pair 是Kotlin标准库中的类,它表示已对元素。Pair和to 都使用了泛型,这里为了简单都省略了他们
infix fun Any.to(other: Any) = Pair(this, other)

fun testPair() {
    val (number, name) = 1 to "one"
    println("this is $number and $name")
}

4、局部函数

为了让代码更简洁,Kotlin提供了局部函数的功能:* 局部函数指的是 可以在函数中嵌套这些提取的函数,这样既可以获得所需的结构,也无须额外的语法开销

先看下一个简单的例子,稍后使用局部函数改造,体验局部函数:
class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException("Can‘t save user[${user.id}] with empty name")
    }
    if (user.address.isEmpty()) {
        throw IllegalArgumentException("Can‘t save user[${user.id}] with empty address")
    }
    println("save user successful!")
}
* 提取局部函数避免重复
fun saveUser1(user: User) {
    fun validate(user: User, value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException("Can‘t save user[${user.id}] with empty $fieldName ")
        }
    }
    validate(user, user.name, "Name")
    validate(user, user.address, "address")
    println("save user successful!")
}
* 可以在局部函数总直接访问外部函数的参数!!
fun saveUser2(user: User) {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            //可以在局部函数总直接访问外部函数的参数!!
            throw IllegalArgumentException("Can‘t save user[${user.id}] with empty $fieldName ")
        }
    }
    validate(user.name, "Name")
    validate(user.address, "address")
    println("save user successful!")
} 
* 提取逻辑到扩展函数
fun User.validateBeforSave() {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException("Can‘t save user[${this.id}] with empty $fieldName ")
        }
    }
    validate(name, "Name")
    validate(address, "Address")
}

  


 

原文地址:https://www.cnblogs.com/mauiie/p/Kotlin_Fun.html

时间: 2024-10-09 11:02:24

Kotlin学习与实践 (三)fun 函数的相关文章

JavaScript学习总结(三、函数声明和表达式、this、闭包和引用、arguments对象、函数间传递参数)

一.函数声明和表达式 函数声明: function test() {}; test();    //运行正常 function test() {}; 函数表达式: var test = function() {}; test;    //undefined test();   //TypeError var test = function() {}; 命名函数的赋值表达式: var test = function bar() { test();    //正常运行 }; test();    /

Kotlin学习与实践 (四)类、接口

1.类的继承结构 接口 * Kotlin的类和接口与Java的有些地方不一样:* Kotlin的声明默认是public final的.* Kotlin嵌套的类默认不是内部类:它没有包含对外部类的隐式引用 等* Kotlin也一样是使用interface来声明接口 * 如下: 声明一个简单的接口 interface Clickable { fun click() fun longPress() = println("longPress") //接口中的抽象方法也可以有默认的实现,有了默认

Kotlin学习与实践 (九)带接收者的lambda及Java的函数式接口

带接收者的lambda * 在lambda 函数体内可以调用一个不同对象的方法,而且无须借助任何额外限定符:这种能力再Java中是找不到的.* 这样的lambda叫做"带接收者的lambda" 先举个普通函数作为反例: fun alphabet(): String { val result = StringBuilder() for (letter in 'A'..'Z') { result.append(letter) } result.append("\nNow ,I k

Kotlin学习与实践 (十)Kotlin的可控性

可空类型 * 类型就是数据的分类.决定了该类型可能的值,以及在该类型值上可以完成的操作. * 与Java不同,Kotlin对可空类型的显示的支持.可空类型是Kotlin类型系统中帮助避免NullPointException错误的特性.* 这是一种指出你的程序中那些变量和属性允许为null的方式. * 可空类型和非可空类型的对象在运行时没有什么区别,可空类型并不是非空类型的包装.所有检查都是在编译时期,所以Kotlin的可空类型并不会在运行时带来额外的开销.先来看看Java中的空指针是怎么造成的

2017python学习的第三天函数

函数就其实可以看作是一个被定义变量 只不过变量定义在内存里面的是一些值 而函数被定义在内存里面就是一些要执行的语句的字符串 函数需要被调用的时候才会运行. 局部变量和全局变量 局部变量就是在函数体内的变量,他和全局表里最大的区别就是作用域 局部变量的作用域就是在函数体内的,而全局变量就全局作用的

JS学习笔记(三)函数

js函数的声明方式为 function 函数名(参数列表) { // 函数体 return 返回值; } 调用 函数名(); (js中花括号喜欢用这种方式,因为在以前的浏览器中会在每行js代码的末尾添加分号,但现在的浏览器一般已经兼容这种写法,但仍保留这种习惯.) 需要注意的是只有在js的函数中声明的对象才具有块级作用域.函数中变量一定要定义,否则就是全局变量. 在解释js的时候,会先将其读入内存进行解释,再一步一步的从上到下的执行代码.js的函数也是变量值,即可以把函数赋值给变量. 1 fun

20170417学习find、replace、abs三个函数

1.编程python学习了三个函数 ①find(str,pos_start,pos_end) 解释: str:被查找"字串" pos_start:查找的首字母位置(从0开始计数.默认:0) pos_end: 查找的末尾位置(默认-1) 返回值:如果查到:返回查找的第一个出现的位置.否则,返回-1. search = '168'num_a = '1386-168-0006'num_b = '1681-222-0006'print(search + ' is at ' + str(num_

转MYSQL学习(三) 函数

这一节主要介绍MYSQL里的函数,MYSQL里的函数很多,我这里主要介绍MYSQL里有而SQLSERVER没有的函数 数学函数 1.求余函数MOD(X,Y) MOD(X,Y)返回x被y除后的余数,MOD()对于带有小数部分的数值也起作用,他返回除法运算后的精确余数 SELECT MOD(31,8) 2.四舍五入函数TRUNCATE(X,Y) TRUNCATE(X,Y)返回被舍去至小数点后y位的数字x.若y的值为0,则结果不带有小数点或不带有小数部分. 若y设为负数,则截去(归零)x小数点左边起第

Google App Engine 学习和实践

这个周末玩了玩Google App Engine,随手写点东西,算是学习笔记吧.不当之处,请多多指正. 作者:liigo,2009/04/26夜,大连 原创链接:http://blog.csdn.net/liigo/archive/2009/04/26/4127055.aspx 转载请注明出处:http://blog.csdn.net/liigo 一,怎么想起来玩Google App Engine了呢? 近期想写一个小程序,以便在公司的电脑和家里的电脑之间随时共享数据.但是没有现成的server