The previous article describes what generics are, why generics are used, and how generics are used, and I believe you have a basic understanding of generics, and this article will continue to explain the use of generics, giving you a better grasp and a deeper understanding of generics.
After the introduction of generics in the previous article, do you feel that the generic type is very useful? Both eliminate the unsafe type of object conversion, but also can be very convenient to type Object access, but, wait, there is no consideration of such a situation.
We define a fruit class first:
Public class Fruit { private String name; Public Fruit (String name) { this. Name = name; } Public String GetName () { return name; } Public void setName (String name) { this. Name = name; }}
Then define an Apple class:
Public class extends fruit{ public Apple (String name) { super(name);} }
Next, define a generic container:
public class genericholder<t> { private T Obj public Genericholder () {} public Genericholder (T obj) { this . obj = obj; public T getobj () { return obj; public void Setobj (T obj) { this . obj = obj; }}
Let's start our test:
Public classTest {/*** Eat fruit *@paramFruitholder*/ Public Static voidEatfruit (genericholder<fruit>Fruitholder) {System.out.println ("I'm eating" +fruitholder.getobj (). GetName ()); } Public Static voidMain (String args[]) {//It's a bag labeled with fruit.Genericholder<fruit> Fruitholder =NewGenericholder<fruit>(); //This is an apple-labeled Bag .Genericholder<apple> Appholder =NewGenericholder<apple>(); //This is a fruitFruit Fruit =NewFruit ("Fruit"); //This is an appleApple Apple =NewApple ("Apple"); //now let's put the fruit in .fruitholder.setobj (fruit); //call a way to eat fruitEatfruit (Fruitholder); //a fruit-labeled bag with fruit, of course, no problem .//now let's put the fruit sub-apple into this bag and seeFruitholder.setobj (Apple); //It is also possible, in fact, this time will be automatic upward transformation, apple into the fruit type, and then passed into the Fruitholder//but it's no longer possible to assign a value to Redapple.//because the label of the bag is fruit, the object that is taken out can only be assigned to the variables of the fruit class.//failed to compile detection redapple = Fruitholder.getobj ();Eatfruit (Fruitholder); //put the Apple label, naturally only put the AppleAppholder.setobj (Apple); //I can't pass appholder into Eatfruit .//because Genericholder<fruit> and genericholder<apple> are two different kinds.//Eatfruit (appholder); }}
Operation Result:
I'm eating the fruit I'm eating an apple
Here, when we pass the Eatfruit method into the Fuitholder, it can be compiled normally, but if the appholder is passed in, it cannot be compiled because as a parameter,genericholder<fruit> and Genericholder<apple> is two different types, so it can't be compiled, so the problem comes if I want the Eatfruit method to handle genericholder<fruit> and What about two types of genericholder<apple>? And this is also very reasonable demand, after all, Apple is a fruit subclass, can eat fruit, why not eat apples??? If you want to overload this method once, it would be a bit of a fuss (and in fact, it can't be compiled, after the specific reason will be explained).
In the logic of the Code:
- Apple is-a fruit
- An apple plate not-is-a a plate of fruit.
At this point, the generic boundary symbol has its niche. Let's look at the effect first:
Public classTest {/*** Eat fruit *@paramFruitholder*/ Public Static voidEatfruit (genericholder<?extendsFruit>Fruitholder) {System.out.println ("I'm eating" +fruitholder.getobj (). GetName ()); } Public Static voidMain (String args[]) {//It's a bag labeled with fruit.Genericholder<fruit> Fruitholder =NewGenericholder<fruit>(); //This is an apple-labeled Bag .Genericholder<apple> Appholder =NewGenericholder<apple>(); //This is a fruitFruit Fruit =NewFruit ("Fruit"); //This is an appleApple Apple =NewApple ("Apple"); //now let's put the fruit in .fruitholder.setobj (fruit); //call a way to eat fruitEatfruit (Fruitholder); //put the Apple label, naturally only put the AppleAppholder.setobj (Apple); //At this time can smoothly put Appholder into EatfruitEatfruit (Appholder); }}
Operation Result:
I'm eating the fruit I'm eating an apple
Here we just use a little bit of magic, change the parameter type to GENERICHOLDER< Extends Fruit>, so you can pass the parameters of the genericholder<apple> type smoothly, how about? Very useful, this is the generic boundary character, with < Extends fruit> in the form of an expression. The meaning of a boundary character is defined by a boundary, which is used here? Indicates that the passed-in generic type is not a fixed type, but rather conforms to all types of the rule scope, with the extends keyword defining an upper boundary, that is, here? can represent any type that inherits from the fruit, you may ask, why is the upper boundary, good question, a picture wins thousand words:
From this graph, we can see the concept of the "upper boundary" very well. There is the upper boundary, the nature has the lower boundary, the generic pattern uses the shape like < Super Fruit> the way to use the next border, at this time,? can only represent fruit and its parent class.
(these two charts are to pull over, do not scold me lazy.) )
These two methods basically solve our previous problems, but at the same time, there are certain limitations.
1. upper bound < extends t> cannot be deposited, only outward
do not be too confused, in fact, very good understanding, because the compiler only know that the container is fruit or fruit subclass, but do not know what the specific type, so when it is not possible to determine whether the type of data to be deposited with the type of container species consistent, so will be rejected set operation.
2. Nether < Super t> can only be assigned to an object variable without affecting the
Because the compiler only knows that it is fruit or its parent class, which in fact relaxes the type limit, the parent of the fruit can be saved to objects of type object, but when taken, it can only be used as object objects.
So if you need to read out frequently, then use < Extends T> if it is necessary to take out frequently, use < Super t>.
at this point, this chapter is complete, welcome to continue to pay attention!
"Getting Started with Java" DAY15 Java generics--generic wildcard characters and upper and lower bounds