Kotlin 学习笔记(五)类 数据,密封,泛型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27948659/article/details/82391070

前言
本文章只是用于记录学习,所以部分地方如果有错误或者理解不对的地方,麻烦请指正。

Kotlin 学习笔记(四)

1. 数据类

  在java 中我们通常会创建很多 Bean 类来存储 数据,在kotlin 中有专门的数据类,“data”

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

数据类必须满足几个条件

  1. 主构造函数需要至少有一个参数;
  2. 主构造函数的所有参数需要标记为 val 或 var;
  3. 数据类不能是抽象、开放、密封或者内部的;

数据类 也为我们自动生成了部分代码:

  1. equals()/hashCode() 对;
  2. toString() 格式是 “User(name=John, age=42)”;
  3. componentN() 函数 按声明顺序对应于所有属性;
  4. copy() 函数

  在 JVM 中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。

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

  下面我们使用代码简单联系一下

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


fun main(args: Array<String>) {
    var json = User(name = "ymc",age = 1)
    val json1 = json.copy(age = 2)
    println(json1)  // 默认调用 User的 tostring()
}

   在java 中我们通常想要赋值一个值,但是只需要改变某一项的值的数据信息,kotlin 中的 copy函数,只需要传入不一样的数据,就会自动化改变,并返回给你修改后的所有数据信息。

2.密封类

  密封类,可以理解为枚举,规定了有限个类型,不可以存在其他类型,但枚举每个枚举常量只存在一个示例,但是密封类的子类可以有多个示例,所以可以将密封类看做是枚举的拓展,基于枚举,高于枚举,青出于蓝而胜于蓝。

  声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明。eg:

// 密封类
sealed class Expr

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

  相对于密封类的子类 必须要在一个文件中,扩展密封类子类的类(间接继承者)可以放在任何位置,而无需在同一个文件中。

扫描二维码关注公众号,回复: 3237080 查看本文章

密封类注意点:

1.一个密封类是自身抽象的,它不能直接实例化 ,但是可以有抽象(abstract)成员。
2.密封类不允许有非-private 构造函数(其构造函数默认为 private)。

  使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。

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

3. 泛型

  kotlin中的泛型和 java 中的差不多,但是很多方面更加简洁。

// kotlin中的 泛型
class Box<T>(t: T) {
    var value = t
}

如果我们要创建

val box: Box<Int> = Box<Int>(1)
// 1 具有类型 Int,所以编译器知道我们说的是 Box<Int>。
val box = Box(1) 

在学习下边Kotlin 的 泛型特性的时候,我们先回顾一下 java 中的 泛型 使用

  1. 通配符上界,只能从中读取元素,不能添加元素,称为生产者(Producers),用< ? extends T>表示。
  2. 通配符下界,只能添加元素,不能直接读取下界类型的元素,称为消费者(Consumers),用< ? super T>表示。

3.1 通配符上界

  < ? extends T>(T表示通配符的上界),表示可以接收T以及T的子类参数,也就是说可以安全的读取到T的实例,事实上所有的集合元素都是T的子类的实例,但不能向其添加元素,因为没法确定添加的实例类型跟定义的类型是否匹配

List<String> strs = new ArrayList<String>();
strs.add("0");
strs.add("1");
List<? extends Object> objs = strs;

objs.get(0); // 可以获取

objs.add(1); // 但是添加的时候报错

  经过本人测试,不管添加 Int ,String 类型都提示无法添加,上面的例子说明了objs可以读取值,但是再往objs里面添加值的时候,就会出错,没法确定添加的实例类型跟定义的类型是否匹配。

3.2 通配符下界

< ? super T>,其中T就表示通配符的下界。
举个栗子:Collection< ? super String>是Collection< String>的父类型,所以可以直接add和set,但是get的时候获取到的类型是Object而不是String类型。

List<String> strs = new ArrayList<String>();
strs.add("0");
List<? super String> objs = strs;
objs.add("1");
objs.set(0, "2");
// 得到Object类型,如果想要String 还需要强转
Object s = objs.get(0);

Kotlin 中的泛型

  不管是Java还是Kotlin,泛型都是使用擦除来实现的,这意味着当你在使用泛型时,任务具体的类型信息都被擦除的,你唯一知道的就是你再使用一个对象。比如,Box和Box在运行时是想的类型,都是Box的实例。在使用泛型时,具体类型信息的擦除是我们不不懂得不面对的,在Kotlin中也为我们提供了一些可供参考的解决方案:、

  1. 类型协变
  2. 类型投射
  3. 泛型约束

3.3 类型协变

  假设我们有一个泛型接口Source< in T, out R >, 其中T由协变注解in修饰,R由协变注解Out修饰.

internal interface Source<in T, out R> {

    // in 函数,可以当做参数使用,消费,但是不能作为返回值
    fun mapT(t: T): Unit

    // out 函数,不能用来当参数,不能消费,但是可以作为返回值
    fun nextR(): R
}

in T:     来确保Source的成员函数只能消费T类型,而不能返回T类型
out R:  来确保Source的成员函数只能返回R类型,而不能消费R类型

  从上面的解释中,我们可以清楚的知道了协变注解in和out的用意,其实际上是定义了类型参数在该类或者接口的用途,是用来消费的还是用来返回的,对其做了相应的限定。

3.4 类型投射

  从上述代码中我们了解到 泛型的 in 和 out 的使用,下面我们通过一段 代码了解 到底什么是 类型投射

fun copy(from: Array<out String>, to: Array<Any>) {
    // ...
}

fun fill(dest: Array<in String>, value: String) {
    // ...
}

  from的泛型参数使用了协变注解out修饰,意味着该参数不能在该函数中消费,在该方法中 禁止 对该参数进行任何操作。

  对于fill函数中,dest的泛型参数使用了协变注解in修饰,Array与Java的 Array < ? super String> 相同, 也就是说, 你可以使用CharSequence数组,或者 Object 数组作为 fill() 函数的参数、

  这种声明在Kotlin中称为类型投射(type projection),类型投射的主要用于对参数做了相对因的限定,避免了对该参数类的不安全操作。

3.5 泛型函数

  类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:

fun <T> singletonList(item: T): List<T> {
    // ……
}

fun <T> T.basicToString() : String {  // 扩展函数
    // ……
}

// 调用方式 指定 类型为 int
val l = singletonList<Int>(1)

猜你喜欢

转载自blog.csdn.net/qq_27948659/article/details/82391070