Personal understanding:
1. Inverter in upward compatible class
2. Covariant out backward compatible classes
In object-oriented programming, especially when using generics, the in
and out
keywords are used to restrict covariance and contravariance of type parameters sex.
-
in
Keywords (contravariant):in
Keyword used to mark contravariance of generic type parameters. Contravariance means that you can use the base class or superclass of the specified type as the parameter type of the method.- When a generic class or interface uses the
in
keyword to qualify type parameters, the generic type can be assigned to a more general type. - For example, if you have a write-only repository, you cannot query it for information, you can only add animals to it. In this case, the generic type parameter can be declared as
in Animal
, indicating that the type parameter can beAnimal
or any of its base classes.
-
out
Keywords (covariance):out
Keyword used to mark covariance of generic type parameters. Covariance means that you can use a subclass or derived class of the specified type as the return type of the method.- When a generic class or interface uses the
out
keyword to qualify type parameters, the generic type can be assigned to a more specific type. - For example, if there is a read-only repository, you cannot add animals through it, only query information. In this case, the generic type parameter can be declared as
out Animal
, indicating that the type parameter can beAnimal
or any of its derived classes.
By using the in
and out
keywords, we can restrict the covariance and contravariance of the type in the generic type parameters to ensure that the type safety. The advantage of this is that it allows easier reuse and flexibility of generic types.
Code example:
// 声明一个只读存储库接口,用于查询动物信息
interface ReadOnlyRepository<out T> {
fun getAll(): List<T>
fun getById(id: String): T?
}
// 声明一个只写存储库接口,用于添加动物
interface WriteOnlyRepository<in T> {
fun add(item: T)
}
// Animal类作为基类
open class Animal(val name: String)
// Dog类继承自Animal
class Dog(name: String) : Animal(name)
// Cat类继承自Animal
class Cat(name: String) : Animal(name)
// 只读存储库实现
class ReadOnlyAnimalRepository : ReadOnlyRepository<Animal> {
private val animals = listOf(Animal("Lion"), Dog("Buddy"), Cat("Whiskers"))
override fun getAll(): List<Animal> {
return animals
}
override fun getById(id: String): Animal? {
return animals.find { it.name == id }
}
}
// 只写存储库实现
class WriteOnlyAnimalRepository : WriteOnlyRepository<Animal> {
private val animals = mutableListOf<Animal>()
override fun add(item: Animal) {
animals.add(item)
}
}
fun main() {
val readOnlyRepo: ReadOnlyRepository<Animal> = ReadOnlyAnimalRepository()
val writeOnlyRepo: WriteOnlyRepository<Dog> = WriteOnlyAnimalRepository()
val allAnimals = readOnlyRepo.getAll()
println("All Animals:")
allAnimals.forEach { animal ->
println("- ${animal.name}")
}
val dog1 = Dog("Max")
writeOnlyRepo.add(dog1)
println("\nAdded Dog:")
val addedDog = readOnlyRepo.getById(dog1.name)
println("- ${addedDog?.name}")
val cat1 = Cat("Misty")
// writeOnlyRepo.add(cat1) // Compilation Error: Type mismatch. Required: Dog, Found: Cat
}
In the code example above, we defined two repository interfaces: ReadOnlyRepository
and WriteOnlyRepository
. ReadOnlyRepository
The interface declares a return typeout T
, indicating that it can only query (read) animal information. WriteOnlyRepository
The interface declares the parameter type in T
, indicating that it can only add (write) animals.
Then, we created a ReadOnlyAnimalRepository
class to implement the ReadOnlyRepository<Animal>
interface for querying animal information. Similarly, we also created a WriteOnlyAnimalRepository
class to implement the WriteOnlyRepository<Animal>
interface for adding animals.
In the main
function, we first assign ReadOnlyAnimalRepository
to the readOnlyRepo
variable, which is legal because < /span> interface. ReadOnlyAnimalRepository
implements the ReadOnlyRepository<Animal>
Then, we assign WriteOnlyAnimalRepository
to the writeOnlyRepo
variable, which is also legal because WriteOnlyAnimalRepository
implements a> method to add variable, we can still only use the WriteOnlyRepository<Animal>
interface. Note that although we assign it to the writeOnlyRepo
add