接口的基本使用
- 接口的所有属性必须要被override,一般情况下不会在接口中定义属性,带属性和方法的情况选用抽象类更合适.
interface InterfaceTest {
val name:String
get() = "hch"
var age:Int
fun sayHello():String
}
class Impl(override var age:Int):InterfaceTest{
override val name: String
get() = "${super.name} impl"
override fun sayHello(): String {
return "$name at $age say hellos"
}
}
var im = Impl(20)
println(im.sayHello())
- 接口中的属性可以有初始值,有初始值的属性必须申明为val,且不能有set方法
- 实现类可以复写接口中的常量初始值,并且可以改为变量类型
interface InterfaceTest{
val name:String
get() = "hch"
var age:Int
fun sayHello():String
}
class Impl(override var age:Int):InterfaceTest{
override var name: String
get() = "${super.name} impl"
set(value) {
name = value
}
override fun sayHello(): String {
return "$name at $age say hellos"
}
}
抽象类
abstract class Toy(var price:Int){
abstract fun play()
}
class ToyTree(var name:String):Toy(100 ){
override fun play() {
println("play toys with $name")
}
}
多重继承
class ToyTree(override var name:String):Toy(100 ),InterfaceTest{
override fun play() {
println("play toys with $name")
}
override var age: Int
get() = 10
set(value) {
age = 10
}
override fun sayHello(): String {
return "toy say hello"
}
}
泛型
- 泛型约束 T:Toy
- 泛型返回 fun fetch():T?
- 泛型在kotlion中都被转化为object类型
class Game<T:Toy>(var item:T){
var isNew:Boolean = false
fun fetch():T?{
return item.takeIf { isNew }
}
fun <R> test(f:(T)->R):R?{
return f(item)
}
}
var g1 = Game(tree)
g1.test {
var tree = it
"ok".takeIf { tree.age == 10 }
}
可变参数 vararg
作用等同于java中的...
class TestB<T>(vararg item: T){
// 注意out
var p:Array<out T> = item
fun test(): Unit {
println(p.size)
}
//运算符[]重载
operator fun get(index:Int):T? = if (index < p.size) p[index] else null
}
var t1 = TestB("aa" , "bb" , "cc")
t1.test()
println(t1[0])
//等价于
public final class TestB {
@NotNull
private Object[] p;
@NotNull
public final Object[] getP() {
return this.p;
}
public final void setP(@NotNull Object[] var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.p = var1;
}
public final void test() {
int var1 = this.p.length;
boolean var2 = false;
System.out.println(var1);
}
@Nullable
public final Object get(int index) {
return index < this.p.length ? this.p[index] : null;
}
public TestB(@NotNull Object... item) {
Intrinsics.checkNotNullParameter(item, "item");
super();
this.p = item;
}
}
out (协变)
如果泛型类只将泛型作为返回,那么使用out,可以将这个类称为生产类,它主要用于生产和输出泛型对象
interface Production<out T> {
fun produce():T?
}
in(逆变)
如果泛型类只是将泛型作为输入消费,则可将该类称为消费类,它用于消费输入的泛型对象
interface Consumer<in T>{
fun consume(item:T)
}
invariant(不变)
如果泛型类既将泛型作为输入,又作为输出,则不需要使用in和out
interface ProductionConsumer<T>{
fun produce():T?
fun consume(item:T)
}
in和out的作用
- 提升程序的扩展性和使用的便利性
- 将父泛型转化给子泛型用in描述
- 将子泛型转化给父泛型用out描述
- 这里加上in/out后,使用上并不会出现类型转化的错误,原因如下图
输入的时候,强化为更小的子类型.因为子类型肯定是父类型. 输出的时候,放大为父类型,因为子类型肯定是父类型.
out用法
interface Production<out T> {
fun produce():T?
}
class FoodStore:Production<Food> {
override fun produce(): Food? {
return Food()
}
}
class FastFoodStore:Production<FastFood> {
override fun produce(): FastFood? {
return FastFood()
}
}
class BurgerStore:Production<Burger> {
override fun produce(): Burger? {
return Burger()
}
}
//out 将子泛型对象转化为父泛型对象
var p1:Production<Food> = FoodStore()
var p2:Production<Food> = FastFoodStore()
var p3:Production<Food> = BurgerStore()
in用法
如果没有用in关键字,会提示类型转换错误.
interface Consumer<in T>{
fun consume(item:T)
}
class People:Consumer<Food> {
override fun consume(item: Food) {
}
}
class ModernPeople:Consumer<FastFood> {
override fun consume(item: FastFood) {
}
}
class American:Consumer<Burger> {
override fun consume(item: Burger) {
}
}
//in 将 父泛型对象转化为子泛型对象
var c1:Consumer<Burger> = People()
var c2:Consumer<Burger> = ModernPeople()
var c3:Consumer<Burger> = American()
c1.consume(Burger("maila"))
c2.consume(Burger("shutiao"))
c3.consume(Burger("jikuai"))
识别泛型的类型inline + reified
- 正常情况下,kotlin不允许检查泛型类型.因为泛型类型会擦除,也就是泛型在运行期间是不可知的.加了reified后,会保留泛型的类型.
- 正常情况下kotlin会将泛型转化为object,当加了reified后,会根据推断的类型,将泛型转化为特定的具体类型保存下来.
class RandomStore2{
inline fun <reified T:Food> random(vararg foods:Food , backup:()->T):T{
var inputs:Array<out Food> = foods
var random = inputs.random()
//判断是否泛型类型
return if (random is T) random else backup()
}
}
var f1 = Food()
var f2 = FastFood()
var f3 = Burger("aaa")
var r = RandomStore2()
var result = r.random(f1,f2,f3){
Burger("abc")
}
println(result.name)
类的扩展函数
在不修改类的情况下,给类增加功能.扩展可用于自定义类,也可以用于系统类扩展,如string,List等.
fun String.addExt():String{
return "$this ${"!".repeat(3)}"
}
fun Any.easyPrint() = println(this)
println("test".addExt())
13.easyPrint()
//等价于
public final class ExtTestKt {
@NotNull
public static final String addExt(@NotNull String $this$addExt) {
Intrinsics.checkNotNullParameter($this$addExt, "$this$addExt");
return $this$addExt + ' ' + StringsKt.repeat((CharSequence)"!", 3);
}
public static final void easyPrint(@NotNull Object $this$easyPrint) {
Intrinsics.checkNotNullParameter($this$easyPrint, "$this$easyPrint");
boolean var1 = false;
System.out.println($this$easyPrint);
}
public static final void main() {
String var0 = addExt("test");
boolean var1 = false;
System.out.println(var0);
easyPrint(13);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
泛型扩展函数 T.extension
可以接受任何类型的扩展,还可以保留类型信息
如图所示,我们希望easyPrint后能够自动推断出来返回类型是string,但是目前kotlin编译器做不到.默认情况下Any被转化为object类型.
因此衍生出来泛型扩展函数,能够保留类型信息.系统函数如let,also,apply,with等都是利用泛型扩展函数来实现.
fun String.addExt():String{
return "$this ${"!".repeat(3)}"
}
fun <T> T.easyPrint():T{
println(this)
return this
}
fun main() {
println("test".addExt())
"abc".easyPrint().addExt().easyPrint()
}
//等价于
public static final void main() {
String var0 = addExt("test");
boolean var1 = false;
System.out.println(var0);
easyPrint(addExt((String)easyPrint("abc")));
}
两种写法实现上的区别
fun <T> T.easyPrint():T{
println(this)
return this
}
"abc".easyPrint().addExt().easyPrint()
// 有做类型保留等价于
easyPrint(addExt((String)easyPrint("abc")));
fun Any.easyPrint():Any{
println(this)
return this
}
"abc".easyPrint()
// 没有类型保留等价于
easyPrint("abc");
public static final Object easyPrint(@NotNull Object $this$easyPrint) {
Intrinsics.checkNotNullParameter($this$easyPrint, "$this$easyPrint");
boolean var1 = false;
System.out.println($this$easyPrint);
return $this$easyPrint;
}