Kotlin学习之泛型(协变和逆变实战)

26 篇文章 1 订阅
4 篇文章 0 订阅

 首先看看在Java中协变和逆变的栗子

public class Animal {
}

public class Cat extends Animal{
}

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main4);
       
   List<Cat> cats = new ArrayList<>();
   List<? extends Animal> animals= cats;
}

这样是可以赋值成功的 

 这样是编译失败的,因为animal的类型是List<Animal>或者List<Animal的子类>,如果把new Cat放进去了,那么把取出来的时候到底是不是Cat类型取决于声明的泛型类型,这里根本不知道真正的类型是什么,只知道是List<Animal>或者List<Animal的子类>,那么可能是Cat 也可能是Dog。所以协变的特点是就是读取操作,写入的操作是不被允许的。那么逆变是可以写入,但是不能读取。

下一个栗子:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main4);
       
   List<Animal> animals1 = new ArrayList<>();
   List<? super Animal> animals2 = animals1;
}

animals1 的类型是 List<Animal>,animals2的类型是List<Animal>或者List<Animal的父类型一直到Object>

animals2里面添加new Cat()是可以的,Cat是位于Animal下面的为什么可以?

根据多态,Cat就是Animal,所以这种操作时可以的。如果我读取呢?

不兼容类型,不允许读取,因为animals2的类型是List<Animal>或者List<Animal的父类型一直到Object>都可以,赋值给Animal,显然是不允许的。

Java中 的数组是天然支持协变的,解释下这句话:

List<String> 和List<Object>是不能直接赋值的,但是对于数组来说呢,比如:

   Object[] objects = new String[]{"hello","world"};

这样做是可以的。将String类型的数组赋值给Object类型的数组,是可以的,这是协变。但是是有问题的:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);
        Object[] objects = new String[]{"hello","world"};
        objects[0] = new Object();//编译通过,运行时报数组存储异常
    }

接下去看下Kotlin

class TestClass<A>(private val value: A) {
    
    fun getValue(): A {
        return this.value
    }
}

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)
        val testClass = TestClass<String>("hell world")
        val result = testClass.getValue()
    }

这样写是没有任何问题的,定义好什么类型,就按照泛型来使用

class TestClass<out T>(private val value: T) {

    fun getValue(): T {
        return this.value
    }
}


 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)
        val testClass = TestClass<String>("hell world")
        var myClass: TestClass<Any> = testClass
    }

这个是Kotlin的协变。类似于Java中的这种情况

List<String> list2 = new ArrayList();
List<? extends Object> list = list2;
class TestClass<in T> {

    fun toString(value: T): String {
        return value.toString()
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)
        val testClass = TestClass<Number>()
        var myClass: TestClass<Int> = testClass//父类型赋给子类型
    }

这个是Kotlin的逆变。类似于Java中写这种情况

List<Object> list = new ArrayList();
List<? super String> list2 = list;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Kotlin中的泛型是一种类型变量的机制,它允许我们在不确定具体类型的情况下编写通用代码。Kotlin中的泛型支持协变逆变,以及in和out的关键字。 首先,我们来看一个简单的例,实现一个泛型容器类: ```kotlin class Container<T>(var item: T) { fun getItem(): T { return item } } fun main() { val container = Container<String>("Hello") println(container.getItem()) } ``` 在这个例中,我们定义了一个名为Container的泛型类,它有一个类型参数T。我们可以创建一个Container实例,并将其实例化为一个具体类型。我们使用getItem方法来获取这个容器中的item。 接下来,我们来介绍一下协变逆变。假设我们有两个类: ```kotlin open class Animal { fun makeSound() { println("Making animal sound") } } class Cat: Animal() { fun meow() { println("Meow") } } ``` 我们可以通过一个简单的示例来说明协变逆变: ```kotlin fun main() { val animals: List<Animal> = listOf(Cat(), Animal()) makeSounds(animals) } fun makeSounds(animals: List<Animal>) { for (animal in animals) { animal.makeSound() } } ``` 在这个例中,我们定义了一个List<Animal>类型的变量animals,它包含了一个Cat和一个Animal实例。我们将这个变量传递给了makeSounds函数,该函数接受一个List<Animal>类型的参数。 在makeSounds函数中,我们使用for循环遍历animals列表,并对其中的每个Animal实例调用makeSound方法。由于Cat是Animal的类,因此它也可以被视为Animal类型,因此我们可以将其添加到List<Animal>类型的变量中。 这里的List<Animal>类型就是协变的,因为我们可以将它的类(如Cat)作为参数传递给makeSounds函数。 现在我们来看一下逆变。假设我们有一个接受Animal类型的参数的函数: ```kotlin fun takeAnimal(animal: Animal) { animal.makeSound() } ``` 我们可以将这个函数传递给另一个函数,该函数期望一个Cat类型的参数。在这种情况下,我们可以使用in关键字来表示逆变: ```kotlin fun main() { val cat: Cat = Cat() takeCat(cat) } fun takeCat(cat: Cat) { takeAnimal(cat) } fun takeAnimal(animal: Animal) { animal.makeSound() } ``` 在这个例中,我们定义了一个takeCat函数,它接受一个Cat类型的参数。我们将这个函数传递给了takeAnimal函数,该函数期望一个Animal类型的参数。由于Cat是Animal的类,因此我们可以将Cat类型的参数传递给takeAnimal函数。这里的takeAnimal函数是逆变的,因为它可以接受其超类型(如Animal)的参数。 最后,我们来看一下out和in关键字。我们可以在定义泛型类型参数时使用这些关键字来限制泛型类型参数的使用方式。out关键字用于声明泛型类型参数是协变的,in关键字用于声明泛型类型参数是逆变的。 例如,我们可以定义一个只允许读取的泛型接口: ```kotlin interface ReadOnlyContainer<out T> { fun getItem(): T } ``` 在这个例中,我们使用out关键字来声明泛型类型参数T是协变的。这意味着我们只能从ReadOnlyContainer接口中获取T类型的值,而不能修改它。这样做的好处是可以使我们更加安全地使用泛型类型参数。 类似地,我们可以定义一个只允许写入泛型接口: ```kotlin interface WriteOnlyContainer<in T> { fun setItem(item: T) } ``` 在这个例中,我们使用in关键字来声明泛型类型参数T是逆变的。这意味着我们只能向WriteOnlyContainer接口中设置T类型的值,而不能获取它。这样做的好处是可以避免意外修改泛型类型参数的值。 总结一下,Kotlin中的泛型支持协变逆变,以及in和out关键字。使用协变逆变可以使我们更加灵活地使用泛型类型参数,而使用in和out关键字可以帮助我们更加安全地使用泛型类型参数。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值