Kotlin新手教程六(数据类、密封类、枚举类、泛型)

本文介绍了Kotlin中的数据类,包括其特性如equals(),hashCode()和toString()的自动生成,以及copy()函数的使用。接着讲解了密封类的概念,用于限制类的继承结构。还讨论了枚举类的初始化、实现接口和使用枚举常量的方法。最后,阐述了泛型的基本用法,包括声明、实例化以及类型变异性别的概念。
摘要由CSDN通过智能技术生成

一、数据类

Kotlin中可以创建单独存放数据的类,这些类需要使用data修饰,被称为数据类。

data class User(val name: String, val age: Int)

之前说过由于Any在Kotlin中是所有类的顶级父类,所以创建的User类会有equals();hashCode();toString()方法,且编译器还会主动的从主构造函数中根据所有的声明的属性提取以下函数:componentN() functions(对应于属性,按声明顺序排列);copy()函数
如果上述函数已经被定义或者从超类中继承而来,就不会再生成。
为了保证生成的代码的一致性以及有意义,数据类需要满足以下条件:

  1. 主构造函数中至少包含一个参数
  2. 主构造函数的所有参数必须标记为val或者var
  3. 数据类型不能是抽象、开放、密封、或者内部的
  4. 数据类只能实现接口(1.1之前)

1.复制(copy)

如果说我们对于一些数据,需要复制它并且只改变一部分属性就可以使用copy函数:

	var user1: User= Girl("嫚嫚", 29, 160, "廊坊")
	var user2: User= user1.copy("二帆")//传递第一参数,第二参数默认
	var user3: User= user1.copy("真真", 28, 168, "福建")//传递所有参数
	var user4: User= user1.copy(age = 30) //命名参数,传递指定参数
	println("$user1,$user1,$user1,$user1")
	//结果
	User(name=嫚嫚, age=29, height=160, address=廊坊),
	User(name=二帆, age=29, height=160, address=廊坊),
	User(name=真真, age=28, height=168, address=福建),
	User(name=嫚嫚, age=30, height=160, address=廊坊)

2.数据类与解构声明(Component)

解构就相当于可以使用更简洁的方式定义变量接收数据类中的信息。

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")

3. Pair和Triple

当我们想要一个变量存储两个或三个不同类型的属性时,可以使用定义类的方法解决;但是会显得相对复杂了一些,此时元组可以解决这一问题:
Pair 二元元组:可以携带两个值
Tuple 三元元组:可以携带三个值

val two: Pair<String, Int> = Pair("张三", 18)
val three = Triple(22.3, "李四", true)    //自动类型推断:Triple<Double, String, Boolean>
println("三元元祖:${three.first}${three.second}${three.third}")

可以这样做是因为Triple实现了Serializable接口:

public data class Triple<out A, out B, out C>(
    public val first: A,
    public val second: B,
    public val third: C
) : Serializable {

    /**
     * Returns string representation of the [Triple] including its [first], [second] and [third] values.
     */
    public override fun toString(): String = "($first, $second, $third)"
}

二、密封类

密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。

sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

三、枚举类

枚举类的最基本的用法是实现类型安全的枚举:

enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

每个枚举常量都是一个对象。枚举常量用逗号分隔。

1.初始化

因为每一个枚举都是枚举类的实例,所以他们可以是这样初始化过的:

enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
}

2.在枚举类中实现接口

一个枚举类可以实现接口(但不能从类继承),可以为所有条目提供统一的接口成员实现,也可以在相应匿名类中为每个条目提供各自的实现。只需将接口添加到枚举类声明中即可,如下所示:

enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
    PLUS {
        override fun apply(t: Int, u: Int): Int = t + u
    },
    TIMES {
        override fun apply(t: Int, u: Int): Int = t * u
    };

    override fun applyAsInt(t: Int, u: Int) = apply(t, u)
}

3.使用枚举常量

Kotlin 中的枚举类也有合成方法允许列出定义的枚举常量以及通过名称获取枚举常量。这些方法的签名如下(假设枚举类的名称是 EnumClass):

EnumClass.valueOf(value: String): EnumClass
EnumClass.values(): Array<EnumClass>

如果指定的名称与类中定义的任何枚举常量均不匹配,valueOf() 方法将抛出 IllegalArgumentException 异常。

四、泛型

和Java中类似,Kotlin也提供泛型,为类型安全提供保障,消除类型转换的烦恼。泛型一般可以用于类、接口、方法上。

1.声明一个泛型

class Test<T>(t: T){
    var value=t
}

2.创建实例

fun main() {
    var a: Double=3.2
    var t=Test(a)
}

或者使用这种方式:

fun main() {
    val test:Test<Double> = Test<Double>(3.3)
}

3.型变

1.声明处型变
声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。

使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:

// 定义一个支持协变的类
class Runoob<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}

fun main(args: Array<String>) {
    var strCo: Runoob<String> = Runoob("a")
    var anyCo: Runoob<Any> = Runoob<Any>("b")
    anyCo = strCo
    println(anyCo.foo())   // 输出 a
}

in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型:

// 定义一个支持逆变的类
class Runoob<in A>(a: A) {
    fun foo(a: A) {
    }
}

fun main(args: Array<String>) {
    var strDCo = Runoob("a")
    var anyDCo = Runoob<Any>("b")
    strDCo = anyDCo
}

4.星号投射

有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓"安全地使用"是指, 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。

对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):
关于星号投射,其实就是*代指了所有类型,相当于Any

class A<T>(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)
fun main(args: Array<String>) {
    //使用类    
    val a1: A<*> = A(12, "String", Apple("苹果"))
    val a2: A<Any?> = A(12, "String", Apple("苹果"))   //和a1是一样的
    val apple = a1.t3    //参数类型为Any
    println(apple)
    val apple2 = apple as Apple   //强转成Apple类
    println(apple2.name)
    //使用数组
    val l:ArrayList<*> = arrayListOf("String",1,1.2f,Apple("苹果"))
    for (item in l){
        println(item)
    }
}

上一篇:Kotlin新手教程五(扩展)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

友农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值