5.Kotlin-泛型

1.泛型(generics)
与java类似,kotlin的类页游类型参数:
class Box(t:T){
var value = t
}
一般情况,使用泛型实例,需要类型参数:
val box : Box = Box(1)

如果类型参数可推断出来,可省略类型参数:
val box = Box(1) //1是Int,编译器可推断出Box

2.型变(Variance)
Java泛型中最棘手部分就是通配符类型,但是kotlin没有
所以kotlin通过型变(Variance)弥补:
声明处型变(declaration-site variance)
类型投影(type projections)
为什么Java泛型需要通配符类型?
在Effective Java解释了该问题-第28条:利用有限制通配符来提升api的灵活性。
Java泛型是不型变的,意味着List不是List子类型;
如果List是型变的,如下代码编译正常,但运行时出现异常:
List strs = new ArrayList();
List objs = strs;//错误,Java禁止型变
objs.add(1);
String s = strs.get(0);//运行出现异常
这就是为什么Collection.addAll()实际参数如下:
//Java
interface Collection ...{
//通配符表示包括E在内的所有子类,称为协变
//通配符表示包括E在内的所有父类,称为逆变
void addAll(Collection items);
}
void copyAll(Collection to, Collection from){
//可以让Collection是Collection子类型
to.addAll(from);
}
协变:表示包括E在内的所有子类,泛型对象只能读取,称为生产者
逆变:表示包括E在内的所有父类,泛型对象只能写入,称为消费者。

3.声明处型变
Java泛型的一个例子:
interface Source{
//只有生产者方法,没有消费者方法
T nextT();
}
void demo(Source strs){
//Source没有消费者方法,型变是安全的,但是java并不知道,所以仍然要禁止
//需要声明类型为Source,这是毫无意义的。
Source objects = strs;//错误:在Java中不允许型变
}
1.out修饰符
在kotlin中,可用out修饰类型参数T,确保T只能输出(生产),不被消费
out修饰符称为型变注解,使类型参数协变
abstract class Source{
abstract fun nextT():T
}
fun demo(strs : Source){
val objects : Source = strs//可以型变,因为T是out
}
2.in修饰符
用in修饰类型参数T,确保T只能被消费,不能输出,使类型参数逆变
abstract class Comparable{
abstract fun compareTo(other : T):Int
}
fun demo(x : Comparable){
//1.0拥有Double类,是Number的子类
x.compareTo(1.0)
//Double是Number的子类,父类Number可以被Double消费
val y : Comparable = x
}
助记符:消费者-输入in, 生产者-输出out
由于in/out在类型参数声明处,所以称为声明处型变。
4.使用处型变/类型投影
类型参数T既不是协变,也不是逆变
class Array(val size : Int){
fun get(index:Int):T{}//生产out
fun set(index:Int, value : T)//消费in
}
fun copy(from : Array, to : Array){
assert(from.size == to.size)
for(i in from.indices){
to[i] = from[i]
}
}
val ints : Array = arrayOf(1,2,3)
val anys = Array(3){ "" }
copy(ints,anys)//错误:期望(Array, Array)
1.out确保from中的Any只生产输出,不被消费,对应于Java的Array
2.in 确保dest中的String只能被消费,不能生产输出,对应于Java的Array

5.星投影<*>
如果对类型参数一无所知,可用星投影:
1.对于Foo,T是一个具有上界TUpper的协变类型参数,Foo<*>等价于Foo
当T未知时,可以安全地从Foo<*>读取TUpper的值

2.对于Foo,T是一个逆变类型参数,Foo<*>等价于Foo
当T未知时,没有什么方式可以安全写入Foo<*>

3.对于Foo,T是一个具有上界TUpper的不型变类型参数,
Foo<*>在读取值时等价于Foo,在写入值时等价于Foo

如果有多个类型参数,则每个类型参数都可单独投影:
interface Function 
Function<*, String> 表示Function
Function       表示Function
Function<*,*>            表示Function
注意:星投影非常像Java的原始类型,但是安全。

6.泛型函数
和java类型,kotlin不仅类有泛型,函数也有泛型:
//普通函数
fun  singletonList(item:T): List{
// 
}
扩展函数
fun  T.basicToString() : String{
//
}
//调动泛型函数,在函数名后指定类型参数
val l = singletonList(1)

7.泛型约束
最常见的约束类型是,与Java的对应的上界:
//>冒号之后指定类型上界,只有Comparable子类型可以替代T
fun > sort(list : List){
}
sort(listof(1,2,3))//Int是Comparable子类型
sort(listof(HashMap())) //错误:HashMap不是		      Comparable>子类型  

默认上界是Any?, < : 上界>中只能指定一个上界,如果同一类型参数需要多个上界,需要单独的where语句
fun  cloneWhenGreater(list : List, threshold : T) : List where T : Comparable,T:Cloneable{
return list.filter{ it > threshold }.map{it.clone()}
} 
  

你可能感兴趣的:(Kotlin)