[introduction to Java] day15 revisiting Java generics — Generic wildcards and upper and lower boundaries

The last article introduced what generics are, why generics should be used and how to use generics. I believe you have a basic understanding of generics. This article will continue to explain the use of generics, so that you can have a better grasp and deeper understanding of generics.

After introducing generics in the previous article, do you think generics are very useful? It can not only eliminate the unsafe type conversion of objects, but also facilitate the access of type objects. However, wait a minute, do you consider this situation.

Let's first define a fruit class:

Then define an apple class:

Next, define a generic container:

Let's start our test:

Operation results:

Here, when we pass fuitholder into eatfruit method, it can be compiled normally, but if we pass appholder, it cannot be compiled, because genericholder < fruit > and genericholder < Apple > are two different types as parameters, so they cannot be compiled. Then the problem comes, What if I want the eatfruit method to handle both genericholder < fruit > and genericholder < Apple >? And this is also a very reasonable demand. After all, apple is a subclass of fruit. If you can eat fruit, why can't you eat apple??? If you want to reload this method once, it's a bit of a fuss (and in fact, it can't be compiled. The specific reasons will be explained later).

In the logic of the code:

At this time, the boundary character of generic type has its place. Let's look at the effect first:

Operation results:

Here we just use a little magic to change the parameter type to genericholder so that parameters of genericholder < Apple > type can be successfully passed in. How about it? It's easy to use. This is the boundary character of generic type. Use . The meaning of boundary symbol is naturally to define a boundary, which is used here? Indicates that the incoming generic type is not a fixed type, but all types that conform to the rule range. An upper boundary is defined with the extends keyword, that is, here? It can represent any type inherited from fruit. You may ask why it is the upper boundary. Good question, one picture is better than a thousand words:

From this figure, we can see the concept of "upper boundary". There is an upper boundary, and naturally there is a lower boundary. The shape used in generics is like ,? Can only represent fruit and its parent class.

(these two pictures are picked. Don't scold me for being lazy.)

These two methods basically solve our previous problems, but at the same time, they also have certain limitations.

  1. Upper bound cannot be saved in, but can only be fetched out

Don't be too confused. In fact, it's easy to understand, because the compiler only knows that fruit or its subclass is in the container, but doesn't know what type it is. Therefore, when saving, it can't judge whether the type of data to be stored is consistent with the type of the container, so it will refuse the set operation.

  2. Lower bound fetching out can only be assigned to the object variable and does not affect storing in

Because the compiler only knows that it is fruit or its parent class, this actually relaxes the type restriction. The parent class of fruit can be stored in the object up to the object type, but when it is retrieved, it can only be used as an object object.

So if you need to read out often, use , if you need to take it out frequently, use

So far, the explanation of this article is completed. Welcome to continue your attention!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>