preface
It has been more than a month since I wrote my last article, which is a long time indeed. A lot of things have happened in the past month, I resigned from Tianjin to Beijing, and then worked in the new company for almost a month…
Far from it, far from it… In the last article, I relearned Kotlin’s details that you didn’t notice. When I wrote about generics, I only wrote about the implementation of generics. I wanted to directly write the contravariant and covariant of generics, but I didn’t write it because of space, so I have this article.
The story begins
Friday afternoon, the little brother son to the hands of the work are finished, idle to have nothing to do in the net stroll Kotlin related knowledge, also with hua zi 😂. I read that article I wrote last time…
“Good elder brother, the piece of generics in the article you wrote did not write when talking about contravariant and covariant, you tell me about it. Come, good elder brother, come to gen Hua Zi! It doesn’t cough.”
“Keep it for yourself. I can’t cough anymore. Contravariant and covariant? I’m going to write an article, so I’ll explain it to you to clear my head.”
“Thank you! I was walking around on the Internet and I noticed that Kotlin has two keywords for contravariant and covariant: in and out. What does that mean?”
“Well, contravariant and covariant versions of Kotlin generics feel the same as contravariant covariant versions of Java generics!”
“Ah? Is there a contravariant covariant in Java? …”
Contravariant covariant in Java
“Let’s take a look at the contravariant covariant in Java. Java is easy with Kotlin.”
Come on, something easy first, let’s take it slow!
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void toWork(a) {
System.out.println("I'm a worker"+getName()+"I want to work hard!!"); }}Copy the code
Create a new Person class with two attributes and a toWork() method to implement.
public class Worker1 extends Person {
public Worker1(String name, int age) {
super(name, age);
}
@Override
public void toWork(a) {
System.out.println("I'm a worker."+getName()+"I want to work hard!!"); }}Copy the code
Worker1 inherits from the Person class and overwrites the toWork() method.
public class Worker2 extends Person {
public Worker2(String name, int age) {
super(name, age);
}
@Override
public void toWork(a) {
System.out.println("I'm a 2 worker."+getName()+"I want to work hard too!!"); }}Copy the code
Worker2 is the same as Worker1, with the exception of the class name.
covariance
“Is everything okay now?”
“Good big brother, see what you say, this I can not! Go on.”
“Ok, now ask you a question, you see the following code will report an error?”
List<Person> personArrayList = new ArrayList<>();
personArrayList.add(new Person("aaa".11));
personArrayList.add(new Worker1("bbb".12));
personArrayList.add(new Worker2("ccc".13));
Copy the code
“Definitely not, because Worker1 and Worker2 are subclasses of Person, so it’s ok!”
“Little brother son is good, then look at the following code will report an error?”
public static void main(String[] args) {
List<Person> personArrayList = new ArrayList<>();
personArrayList.add(new Person("aaa".11));
personArrayList.add(new Worker1("bbb".12));
personArrayList.add(new Worker2("ccc".13));
List<Worker1> personArrayList1 = new ArrayList<>();
personArrayList1.add(new Worker1("ddd".14));
List<Worker2> personArrayList2 = new ArrayList<>();
personArrayList2.add(new Worker2("eee".15));
setWork(personArrayList);
setWork(personArrayList1);
setWork(personArrayList2);
}
public static void setWork(List<Person> studentList) {
for (Person o : studentList) {
if(o ! =null){ o.toWork(); }}}Copy the code
“Wait a minute. There’s a lot of code. Let me get this straight. It’s okay to create a set of Persons, then put in the data, then create a set of Worker1 and Worker2, and then call setWork().
“Hey hey, little brother, this is a problem, you see!”
“Ah? Worker1 and Worker2 are subclasses of Person.
Worker1 and Worker2 are subclasses of Person, but List and List are not subclasses of List.
“Good big brother good big brother, quick say how to solve it.”
This brings us to today’s topic —- covariant! So List and List are not subclasses of List, so what we need to do is make List and List subclasses of List, which is pretty simple, just add? Extends is fine.”
public static void setWork(List<? extends Person> studentList) {
for (Person o : studentList) {
if(o ! =null){ o.toWork(); }}}Copy the code
“Is it that simple?”
“Yeah, that’s it. Let’s run it.”
I am worker AAA, I want to work hard!! I am 1 worker BBB, I want to work well!! I am 2 workers CCC, I also want to work well!! I am 1 worker DDD, I want to work well!! I am 2 worker Eee, I also want to work hard!!Copy the code
“Wow! It really can! After that, I write generics and I write all covariant! Then all subclasses can do this!”
“Don’t! There must be limits! Covariant generics can only be retrieved, but cannot be modified. For example, in a List, you can only get, not add.
“Ah? I don’t believe it!”
“Oh, we’ll try it if you don’t believe me!”
“Ah! It really is! No, you’re adding Worker1, but the generic is Person!”
“Just said it! It’s already covariant, it’s ok.”
“Good big brother, you change Peoson try again!”
“Good good, change, you see!”
“Ah? Why ah! Come on, good brother, don’t keep me in suspense!”
“Ha ha ha, ok. So if you think about it, we just changed generics to covariant, which means that the upper bound is Person, but our type is, right? The compiler does not know what type we are giving it, as long as it is derived from a Person class. Therefore, the get object must be a subtype of Person. We could be adding a List, or a List, or a List, so the compiler can’t execute without knowing what type we’re adding, and it’s going to get an error!”
“Oh, I sort of understand that.”
inverter
“In fact, after covariant understanding inverter is simple, and covariant is just the opposite! Let’s look at another method. You see this will report an error!”
List<Person> personArrayList = new ArrayList<>();
personArrayList.add(new Person("aaa".11));
personArrayList.add(new Worker1("bbb".12));
personArrayList.add(new Worker2("ccc".13));
List<Worker1> personArrayList1 = new ArrayList<>();
personArrayList1.add(new Worker1("ddd".14));
setWorker(personArrayList);
setWorker(personArrayList1);
public static void setWorker(List<Worker1> studentList) {
for (Object o : studentList) {
System.out.println("Ha ha"+o.toString()); }}Copy the code
“Well… Woker1 is a subclass of Person, not a subclass of Person Worker1.”
“Good, my little brother is getting smarter, ha ha ha. It is true that it is not possible, for the same reason as you said, but in this case we can use inverse.”
public static void setWorker(List<? super Worker1> studentList) {
for (Object o : studentList) {
System.out.println("Ha ha"+o.toString()); }}Copy the code
“Dear brother, is contravariant and covariant limited?”
“Yeah, contravariant and covariant are the same, types are the same, right? , but? Extends is an upper bound wildcard, and? Super is a lower bound wildcard whose scope includes Worker1 and its parent class. In contrast to covariant, contravariant Worker1 can be added because Worker1 must be a subtype of the unknown type. There is also no restriction on get, which becomes Object, because in Java all types are subclasses of Object. “
“Oh, inverse is not difficult!”
Contravariant covariant in Kotlin
“Kotlin’s contravariant and covariant versions of Java don’t need to be learned. Because it’s almost exactly the same! You say you don’t know Kotlin’s contravariant and covariant, but you don’t know Java’s contravariant and covariant. Take a look at Kotlin’s contravariant and covariant! Let’s start with a couple of classes, same as above, but in Kotlin.”
open class Person(val name: String, val age: Int) {
open fun toWork(a) {
println("I'm a worker$nameI want to work hard!!")}}class Worker1(name: String, age: Int) : Person(name, age) {
override fun toWork(a) {
println("I am a worker$nameI want to work hard!!")}}class Worker2(name: String, age: Int) : Person(name, age) {
override fun toWork(a) {
println("I am a 2 worker$nameI want to work hard too!!")}}Copy the code
Kotlin’s covariant out
“Good eldest brother, said along while, in and out keyword exactly how use you still haven’t said!”
“Don’t worry, just look at the code!”
fun main(a) {
val personArrayList: MutableList<Person> = ArrayList()
personArrayList.add(Person("aaa".11))
personArrayList.add(Worker1("bbb".12))
personArrayList.add(Worker2("ccc".13))
val personArrayList1: MutableList<Worker1> = ArrayList()
personArrayList1.add(Worker1("ddd".14))
val personArrayList2: MutableList<Worker2> = ArrayList()
personArrayList2.add(Worker2("eee".15))
setWork(personArrayList)
setWork(personArrayList1)
setWork(personArrayList2)
}
fun setWork(studentList: List<out Person>) {
for (o in studentList) {
o.toWork()
}
}
Copy the code
“Ah, brother, I seem to understand, this… Covariant is the same as Java, but the key is different!
“Ha, ha, ha, so as long as understand the Java Kotlin actually not difficult! Look again at what’s wrong with setWork().”
“This method reminds us that the out keyword can be omitted! Wrong ah, that omitted will report wrong ah!”
“Hahaha, that’s what Kotlin did for us. Kotlin’s List is read-only, so it must be safe, so when the official List interface is defined it is covariant!”
Inverse theta of Kotlin
“Good big brother, inverse I think I should be able to, I help you rewrite the Java inverse method just now!”
fun setWorker(studentList: MutableList<in Worker2>) {
for (o in studentList) {
println("Ha ha" + o.toString())
}
}
Copy the code
“Yes, it’s as simple as that!
conclusion
“Good brother, why do you think there are contravariant and covariant? Why can’t you just use them all and make it so complicated?”
“Do you know the type erasure of generics?”
“Er… Yes.”
“Because Java generic types have type erasure at compile time, to ensure type safety… Forget it, I’ll talk about it later, it’s a bit much to talk about, I’ll tell you next time! Ready to go off work little brother, tomorrow holiday, ready to do what to go?”
“Going out, of course!
“Don’t go out as much as possible during the pandemic. Off work, gone.”
If this article has helped you, don’t forget the triple link. If there is anything inappropriate or wrong in the description of this article, please mention it, thank you very much. That’s it. I’ll see you later.