习惯用法
data class
我们在用java开发项目的时候会编写很多java bean类,而data class 可以帮助我们简化这一过程,在java文件编译成class文件的过程中,帮助我们重载里面的一些方法。
类提供以下功能:
1.所有属性的 getters (对于 var 定义的还有 setters)
2.equals()
3.hashCode()
4.toString()
5.copy()
6.所有属性的 component1() 、 component2() ……等等
data class Customer(val name: String, val email: String)
可以把所有java bean的类放在一个文件中,一句话就解决了,非常便捷,虽然,data class 给了我们很大的便捷之处,但是如果我们用gson来解析解析json数据生成data class对象还有一些问题,会在另一篇文章讲到
解构声明
有时把一个对象解构成很多变量会很方便
val (name, age) = person
一个解构声明会被编译成以下代码:
val name = person.component1()
val age = person.component2()
密封类
密封类用来表示受限的类继承结构:当一个值为有限集中的 类型、而不能有任何其他类型 时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量 只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以 有子类,但是 所有子类都必须在与密封类自身相同的文件中声明:
扩展函数
声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面 代码为 MutableList<Int> 添加一个 swap 函数:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在一个类中插入新成 员, 仅仅是可以通过该类型的变量用点表达式去调用这个新函数
扩展属性
val <T> List<T>.lastIndex: Int
get() = size - 1
泛型
与 Java 类似,Kotlin 中的类也可以有类型参数:
class Box<T>(t: T) {
var value = t
}
但是如果类型参数可以推断出来,例如从构造函数的参数或者从其他途径,允许省略类型参 数:
val box = Box(1)
// 1 具有类型 Int,所以编译器知道我们说的是 Box<Int>。
型变:
首先,Java 中的泛型是不型变的,这意 味着 List<String> 并不是 List<Object> 的子类型。 为什么这样? 如果 List 不是不型变 的,它就没 比 Java 的数组好到哪去,因为如下代码会通过编译然后导致运行时异常:
//
Java List<String> strs = new ArrayList<String>();
List<Object> objs = strs; //!!!即将来临的问题的原因就在这里。Java 禁止这样!
objs.add(1); // 这里我们把一个整数放入一个字符串列表
String s = strs.get(0); // !!! ClassCastException:无法将整数转换为字符串
kotlin中引入了in和out关键字。
假设有一个泛型接口 Source<T> ,该接口中不存在任何以 T 作为参数的方法,只是方法返 回 T 类型值:
interface Source<T>{
T nextT();
}
那么,在 Source <Object> 类型的变量中存储 Source <String> 实例的引用是极为安全的 ,因为String是Object的子类。但是 Java 并不知道这一点,并且仍然禁止这样操作:
Java
void demo(Source<String> strs) {
Source<Object> objects = strs;
// !!!在 Java 中不允许 // ……
}
为了修正这一点,我们必须声明对象的类型为Source<? extends Object>
在 Kotlin 中,有一种方法向编译器解释这种情况。这称为声明处型变:我们可以标注 Source 的类型参数 T 来确保它仅从 Source<T> 成员中返回(生产),并从不被消费。 为此,我们 提供 out 修饰符:
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs
// 这个没问题,因为 T 是一个 out-参数
……
}
一般原则是:当一个类 C 的类型参数 T 被声明为 out 时,它就只能出现在 C 的成员的输 出-位置,但回报是 C<Base> 可以安全地作为 C<Derived> 的超类。
另外除了 out,Kotlin 又补充了一个型变注释:in。它使得一个类型参数逆变:只可以被消费 而不可以 被生产。逆变类的一个很好的例子是 Comparable :
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型 // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
val y: Comparable<Double> = x // OK!
}
理解为什么这个技巧能够工作的关键相当简单:如果只能从集合中获取项目,那么使用 String 的集合, 并且从其中读取 Object 也没问题 。反过来,如果只能向集合中 放入 项 目,就可以用 Object 集合并向其中放入 String
星投影
有时你想说,你对类型参数一无所知,但仍然希望以安全的方式使用它。 这里的安全方式是 定义泛型类型的这种投影
Kotlin 为此提供了所谓的星投影语法:
1.对于 Foo <out T> ,其中 T 是一个具有上界 TUpper 的协变类型参数, Foo <> 等价 于 Foo <out TUpper> 。 这意味着当 T 未知时,你可以安全地从 Foo <> 读取 TUpper 的值。
2.对于 Foo <in T> ,其中 T 是一个逆变类型参数, Foo <> 等价于 Foo <in Nothing> 。 这意味着当 T 未知时,没有什么可以以安全的方式写入 Foo <> 。
3.对于 Foo <T> ,其中 T 是一个具有上界 TUpper 的不型变类型参数, Foo<*> 对于读 取值时等价于 Foo<out TUpper> 而对于写值时等价于 Foo<in Nothing> 。
泛型约束:
最常见的约束类型是与 Java 的 extends 关键字对应的 上界:
fun <T : Comparable<T>> sort(list: List<T>) { // ……
}
网友评论