Kotlin学习笔记——(六)接口、抽象类、泛型、扩展、集合操作符、与Java互操作性、单例

注:编码工具为IntelliJ

目录

接口

示例

定义变量

示例

变量默认值

抽象类

示例

实现接口

泛型

泛型类

泛型函数

泛型实现自定义map变换

泛型类联合泛型函数实现

泛型函数实现

类型约束

泛型vararg参数

vararg示例

泛型vararg

类型判断

out和in关键字

扩展

扩展函数

扩展属性

对泛型扩展

infix:中缀表达式

结合泛型使用

集合操作符

map

flatMap

filter

zip

与Java的互操作

用可空类型接收Java的数据

@file:JvmName()

@JvmField

@JvmOverloads

@JavaStatic

单例

饿汉式

懒汉式


接口

        接口默认是open的,接口成员默认也是open的。

示例

interface InterfaceTest {
    fun test()
}

class InterfaceTestImpl : InterfaceTest{
    override fun test() {
        println("这是实现自InterfaceTest的方法")
    }
}

fun main() {
    InterfaceTestImpl().test()
}

输出:

这是实现自InterfaceTest的方法

定义变量

        实现类可以通过构造函数参数或者在类内部两种方式重写接口的变量。

        接口内部可以有函数默认实现。

示例

interface InterfaceVariable {
    var name : String
    val des: String

    fun show(){
        println("name = $name, des = $des")
    }
}

class InterfaceVariableImpl(override var name: String,
                            override val des: String) : InterfaceVariable

class InterfaceVariableImpl2(name: String, des: String): InterfaceVariable{
    override var name: String = name
    override val des: String = des
}

fun main() {
    InterfaceVariableImpl("Happy", "Word").show()
    InterfaceVariableImpl2("Terminal", "When").show()
}

输出:

name = Happy, des = Word
name = Terminal, des = When

变量默认值

        接口定义的变量不可以用=直接赋值,但是val修饰的变量可以用get属性赋值。

        有默认值的接口变量,可以重写,也可以不重写。

interface InterfaceVariable2 {
    val randNum: String
        get() = (1..100).shuffled().first().toString()
    val des : String
        get() = "不要问我从哪里来"

    fun show(){
        println("randNum = $randNum, des = $des")
    }
}

class InterfaceVariable2Impl: InterfaceVariable2 // 接口内变量有默认值,则可以不重载

class InterfaceVariable2Impl2(randNum: String, des: String): InterfaceVariable2{
    override val randNum: String = randNum
    override val des: String = des
}

fun main() {
    InterfaceVariable2Impl().show()
    InterfaceVariable2Impl2("hahaha", "song").show()
}

输出:

randNum = 89, des = 不要问我从哪里来
randNum = hahaha, des = song

抽象类

        抽象类默认是open的,抽象类的抽象函数和抽象属性默认也是open的。

        抽象类可以有非抽象成员变量和非抽象函数。

        非抽象成员变量和非抽象函数如果想要被重写,需要添加open关键字。

示例

abstract class Abstract(age: Int) {
    abstract var name: String
    open var age: Int = age
    open fun show() {
        println("age = $age")
    }

    abstract fun show2()
}

class SubAbstract(override var name: String, override var age: Int) : Abstract(age) {
    override fun show2() {
        println("name = $name, age = $age")
    }

    override fun show() {
        println("override show")
    }
}

fun main() {
    val subAbstract = SubAbstract("Sjh", 30)
    subAbstract.show()
    subAbstract.show2()
}

输出:

override show
name = Sjh, age = 30

实现接口

        抽象类可以实现接口,既可以重写接口成员,也可以不重写。

interface Animal{
    fun show()
}

abstract class Cat: Animal

abstract class Dog: Animal{
    override fun show() {
        println("Dog重写了show")
    }
}

class Poodle: Dog()

fun main() {
    Poodle().show()
}

输出:

Dog重写了show

泛型

泛型类

class GenericClass<T>(val t: T){
    fun printAny() = println(t)
}

fun main() {
    GenericClass(123).printAny()
    GenericClass("hello").printAny()
    GenericClass(123.3987f).printAny()
    GenericClass('C').printAny()
    GenericClass(3214.908).printAny()
}

输出:

123
hello
123.3987
C
3214.908

泛型函数

fun <TYPE> getSelf(t: TYPE) = t.takeIf { t != null } ?: "t is null"

fun main() {
    println(getSelf(null))
    println(getSelf("今天星期天"))
    println(getSelf(234))
}

输出:

t is null
今天星期天
234

泛型实现自定义map变换

map变换把一种类型输入,变换为一种类型输出

泛型类联合泛型函数实现

package step_six

class GenericClassMap<I>(val input: I, val isMap : Boolean = true){
    fun <O> map(action:(I)->O) = action(input).takeIf { isMap }
}

fun main() {
    println(GenericClassMap("i love this").map {
        it.uppercase()
    })
}

输出:

I LOVE THIS

泛型函数实现

private fun <I, O> map(input: I, isMap: Boolean = true, action: (I) -> O) 
    = action(input).takeIf { isMap }

fun main() {
    println(map("into the unknown") {
        it.uppercase()
    })
}

输出:

INTO THE UNKNOWN

类型约束

        类似于Java的T extends CharSequence,Kotlin写法为T: CharSequence。

open class SuperObject(val name: String)

open class Human(val humanName: String): SuperObject(humanName)

class Man(val manName: String): Human(manName)

class Woman(val womanName: String): Human(womanName)

class Other(val name: String)

fun <T: Human> show(t: T){ // 只能接收Human及子类对象
    println("name = ${t.humanName}")
}

fun main() {
//    show(SuperObject("super object"))// 编译不通过
    show(Human("human"))
    show(Man("man"))
    show(Woman("woman"))
//    show(Other("other"))// 编译不通过
}

输出:

name = human
name = man
name = woman

泛型vararg参数

        vararg相当于Java的可变参数。

vararg示例

fun show(vararg arr: Int){
    arr[0] = 1
    arr.forEach {
        println(it)
    }
}

fun main() {
    show(-1)
}

输出:

1

泛型vararg

        泛型vararg参数,只能用out修饰的泛型参数数组接收,只能读取其中元素,不能修改。

private fun <T> show(vararg ts: T){
    ts.forEach {
        print("$it ")
    }
}

fun main() {
    show(1, 2, 3)
}

输出:

1 2 3 

类型判断

        泛型参数进行is和as操作的时候,后面要跟可空数据类型,因为泛型可以接收null。

private fun <T> show(t : T){
    if(t is String?){
        println(t?.length ?: null)
    }else if(t is Int?){
        println(t ?: null)
    }else{
        println("其他类型")
    }
}

fun main() {
    show(null)
    show("Hope")
    show(123)
    show('C')
}

输出:

null
4
123
其他类型

out和in关键字

        out:        

                out T相当于Java的? extends T,与Java的不同点是out T只能在类或接口上声明。

                out T在类或接口上声明时,该类的所有函数只能将T类型变量作为返回值,不能作为函数入参。 

        in:

                in T相当于Java的? super T,与Java的不同点是in T只能在类或接口上声明。

                in T在类或接口上声明时,该类的所有函数只能将T类型变量作为入参,不能作为函数返回值。

扩展

扩展函数

        在类的外部定义的类的成员函数,可以和类内部的成员函数一样调用,可以访问类内部成员。

class ExpandTest(val info: String)

fun ExpandTest.show() = println(info)

fun main() {
    ExpandTest("扩展函数").show()
}

输出:

扩展函数

扩展属性

         在类的外部定义的类的成员属性,可以和类内部的成员属性一样调用。

class ExpandField(val info: String)

val ExpandField.infoLength
    get() = info.length

fun main() {
    println(ExpandField("扩展属性").infoLength)
}

输出:

4

注意:

        扩展函数和扩展属性不限制访问范围的话,全局都可以访问。

        如果想让可空类型也可以调用扩展函数或扩展属性的话,则需要对可空类型进行扩展。

        可以将扩展函数和扩展属性定义到单独的文件中,便于查找和维护。

对泛型扩展

        内置函数如:apply、let、run、with、also、takeIf、takeUnless的实现原理。

private fun <T> T.log() = println(this)

fun main() {
    123.log()
    "abc".log()
    'C'.log()
    null.log()
}

输出:

123
abc
C
null

infix:中缀表达式

infix fun String.infixTest(i: Int){
    println("$this:::$i")
}

fun main() {
    "abcd" infixTest 1324
}

输出:

abcd:::1324

结合泛型使用

        应用范围更广,如 to,可以生成任意类型的Pair变量。

private infix fun <T, X> T.union(x: X){
    println("t = $this, x = $x")
}

fun main() {
    8734.2 union "abc"
}

输出:

t = 8734.2, x = abc

tips:导包可以用as取别名,可以提高开发效率,解决包冲突问题。

集合操作符

map

        可以将集合元素做一些转换后添加到另一个集合中。

fun main() {
    listOf("Beijing", "Shanghai", "Nanjing").map {
        it.length
    }.forEach{
        print("$it ")
    }
}

输出:

7 8 7 

flatMap

        可以将集合元素做一些转换,flatMap的lambda必须返回一个集合。

fun main() {
    listOf("张三", "李四", "王五").flatMap {
        listOf("&$it&")
    }.forEach { print("$it ") }
}

输出:

&张三& &李四& &王五& 

filter

        根据过滤条件挑选符合的元素。

fun main() {
    listOf("Hi", "Hello", "What", "World", "Happy")
        .filter {it.contains("H")}
        .forEach { print("$it ") }
}

输出:

Hi Hello Happy 

zip

        合并两个集合,将对应下标的元素封装成一个Pair,最终放到一个新的集合里。

fun main() {
    val words = listOf("Hi", "Hello", "Hope", "Happy", "Work")
    val lengths = listOf(2, 5, 4, 5, 4)
    words.zip(lengths).forEach {
        println("word = ${it.first}, length = ${it.second}")
    }
}

输出:

word = Hi, length = 2
word = Hello, length = 5
word = Hope, length = 4
word = Happy, length = 5
word = Work, length = 4

与Java的互操作

用可空类型接收Java的数据

Java代码:

package communicate_with_kotlin;

public class ProvideValue {

    public static String getInfo(){
        return null;
    }

    public static String getString(){
        return "测试";
    }
}

Kotlin代码:

import communicate_with_kotlin.ProvideValue

fun main() {
    val info: String? = ProvideValue.getInfo()
    val string: String? = ProvideValue.getString()
    println(info?.length ?: "info是空值")
    println(string?.length ?: "string是空值")
}

输出:

info是空值
2

@file:JvmName()

        用于定义Kotlin文件生成的类名,必须写在包名前面。

Kotlin代码:

@file:JvmName("NewClassName")
package step_six

fun jvmName(){
    println("测试JvmName")
}

Java代码:

import step_six.NewClassName;

public class JvmNameTest {
    public static void main(String[] args) {
        NewClassName.jvmName();
    }
}

输出:

测试JvmName

@JvmField

        可以使Java代码可以直接访问Kotlin的变量,而不需要通过get方法。

Kotlin代码:

package step_six

class JvmFieldTest(@JvmField val msg: String)

Java代码:


import step_six.JvmFieldTest;

public class JvmField {
    public static void main(String[] args) {
        System.out.println(new JvmFieldTest("JvmField测试").msg);
    }
}

输出:

JvmField测试

@JvmOverloads

        可以使Java代码能够用Kotlin的默认参数特性。

Kotlin代码:

package step_six

@JvmOverloads
fun show(name: String, age: Int = 99, gender: Char = 'F')
{
    println("name = $name, age = $age, gender = $gender")
}

Java代码:

package communicate_with_kotlin;

import step_six.JvmOverloadsTestKt;

public class JvmOverloads {
    public static void main(String[] args) {
        JvmOverloadsTestKt.show("ss");
        JvmOverloadsTestKt.show("gg", 14);
        JvmOverloadsTestKt.show("h", 30);
    }
}

输出:

name = ss, age = 99, gender = F
name = gg, age = 14, gender = F
name = h, age = 30, gender = F

@JavaStatic

        使Java代码像调用Java的static方法一样调用Kotlin的函数。

Kotlin代码:

package step_six

class JvmStaticTest {
    companion object{
        @JvmStatic
        fun show(){
            println("JvmStatic测试")
        }
    }
}

Java代码:

public class JvmStatic {
    public static void main(String[] args) {
        JvmStaticTest.show();
    }
}

输出:

JvmStatic测试

单例

饿汉式

object EHanSingleton{
    fun show(){
        println("Kotlin饿汉式单例")
    }
}

fun main() {
    EHanSingleton.show()
}

输出:

Kotlin饿汉式单例

懒汉式

package step_six

class LanHanSingleton private constructor(){

    init{
        println("主构造函数执行了")
    }

    companion object{
        val INSTANCE: LanHanSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){
            LanHanSingleton()
        }
    }

    fun show(){
        println("Kotlin懒汉式单例")
    }
}

fun main() {
    LanHanSingleton.INSTANCE.show()
    LanHanSingleton.INSTANCE.show()
}

输出:

主构造函数执行了
Kotlin懒汉式单例
Kotlin懒汉式单例

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>