Implementation of Java generic extensions keyword setting boundary
This article mainly introduces how the placeholder in < > in the generic definition is used with the extends keyword, such as < T extends integer >. Generic definitions exist in these three forms: generic classes, generic interfaces, and generic methods.
Next, this paper will explain the remaining knowledge points with several examples and specific analysis.
Multi boundary analysis of type parameters
For the generic class in this example, the type parameter has multiple boundaries. The practical significance of the following categories: dimension represents the orientation of the object, hascolor represents the color of the object, and weight represents the weight of the object.
interface HasColor { java.awt.Color getColor(); } class Colored<T extends HasColor> { T item; Colored(T item) { this.item = item; } T getItem() { return item; } // The bound allows you to call a method: java.awt.Color color() { return item.getColor(); } } class Dimension { public int x,y,z; } // This won't work -- class must be first,then interfaces: // class ColoredDimension<T extends HasColor & Dimension> { } // Multiple bounds: class ColoredDimension<T extends Dimension & HasColor> { T item; ColoredDimension(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } } interface Weight { int weight(); } // As with inheritance,you can have only one // concrete class but multiple interfaces: class Solid<T extends Dimension & HasColor & Weight> { T item; Solid(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } int weight() { return item.weight(); } } class Bounded extends Dimension implements HasColor,Weight { public java.awt.Color getColor() { return null; } public int weight() { return 0; } } public class BasicBounds { public static void main(String[] args) { Solid<Bounded> solid = new Solid<Bounded>(new Bounded()); solid.color(); solid.getY(); solid.weight(); } } ///:~
class derivedBounded extends Bounded {} class Bounded1 extends Dimension implements HasColor,Weight { public java.awt.Color getColor() { return null; } public int weight() { return 0; } } public class BasicBounds { public static void main(String[] args) { //Solid<Bounded> solid = new Solid<Integer>(new derivedBounded());//给定的具体类型不符合边界 Solid<Bounded> solid1 = new Solid<Bounded>(new derivedBounded());//可以传递具体类型Bounded的子类 //Solid<Bounded> solid2 = new Solid<Bounded>(new Bounded1());//编译报错,因为泛型的静态类型检查 solid1.color(); solid1.getY(); solid1.weight(); } } ///:~
According to the previous article, the specific type specified here in new solid < integer > (New bounded()) does not conform to the requirements of extensions dimension & hascolor & weight of the T-type parameter defined by the generic class, so the compilation will report an error; When passing a value to the constructor, the argument can be a subclass of bound; A class bounded1 that also inherits the same boundary cannot be passed to the constructor because the type has been specified as bounded.
However, when the type parameter has multiple boundaries, how is the Java bytecode handled inside Java:
public static void main(java.lang.String[]); Code: 0: new #2 // class Solid 3: dup 4: new #3 // class Bounded 7: dup 8: invokespecial #4 // Method Bounded."<init>":()V 11: invokespecial #5 // Method Solid."<init>":(LDimension;)V 14: astore_1
From method solid "
":(LDimension;) V you can see that when passing parameters to the solid constructor, the compiler considers the formal parameter as a dimension, which is the compiler's method of processing multiple boundaries. It is always processed as the first boundary, that is, the type is erased as the first boundary. But what about the remaining two boundaries? The first boundary is handled here. Let's take a look at solid Class decompile code to find the answer:
// Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) import java.awt.Color; class Solid<T extends Dimension & HasColor & Weight> { T item; Solid(T item) { this.item = item; } T getItem() { return this.item; } Color color() { return ((HasColor)this.item).getColor();//类型转换为其他边界,再调用方法 } int getX() { return this.item.x; } int getY() { return this.item.y; } int getZ() { return this.item.z; } int weight() { return ((Weight)this.item).weight();//类型转换为其他边界,再调用方法 } }
When the called method does not belong to the first boundary, type conversion is performed to other boundaries. Anyway, t must comply with the extensions dimension & hascolor & weight.
Inherit generic classes with boundary requirements
By observing the above example, it can be seen that the three classes colored, coloreddimension and solid have redundancy in holding objects: they all have the same member variable, the same constructor and the same get function. In terms of type parameters, the boundaries are superimposed in turn. Similarly, the attributes and methods brought by these boundaries are also redundant. Therefore, the following example modifies it to eliminate redundancy through inheritance. Note that the following inherited generic classes have boundary requirements for type parameters:
//HoldItem对边界T没有要求 class HoldItem<T> { T item; HoldItem(T item) { this.item = item; } T getItem() { return item; } } //Colored2对边界T有HasColor的要求 class Colored2<T extends HasColor> extends HoldItem<T> { Colored2(T item) { super(item); } java.awt.Color color() { return item.getColor(); } } //ColoredDimension2对边界T有Dimension & HasColor的要求 class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> { ColoredDimension2(T item) { super(item); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } } //Solid2对边界T有Dimension & HasColor & Weight的要求,不过没有类继承它了 class Solid2<T extends Dimension & HasColor & Weight> extends ColoredDimension2<T> { Solid2(T item) { super(item); } int weight() { return item.weight(); } } public class InheritBounds { public static void main(String[] args) { Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded()); solid2.color(); solid2.getY(); solid2.weight(); } } ///:~
To sum up:
When a generic class inherits another generic class (the former belongs to "define generic class" and the latter belongs to "use generic class") and uses the same type parameter, the type parameter boundary definition of the generic class must be less than or equal to the boundary requirements of the generic class used.
Boundary definition in generic methods
The boundary definition of type parameters in generic methods must also meet the boundary requirements of the generic class used. In this example, generic classes also inherit other generic classes. The analysis is the same as above and will not be repeated. Let's talk about the practical significance of the class: a series of interfaces represent superpowers, and a series of classes represent superheroes. They have a member variable of superpowers.
import java.util.*; interface SuperPower {} interface XRayVision extends SuperPower { void seeThroughWalls(); } interface SuperHearing extends SuperPower { void hearSubtleNoises(); } interface SuperSmell extends SuperPower { void trackBySmell(); } class SuperHero<POWER extends SuperPower> { POWER power; SuperHero(POWER power) { this.power = power; } POWER getPower() { return power; } } class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> { SuperSleuth(POWER power) { super(power); } void see() { power.seeThroughWalls(); } } class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> { CanineHero(POWER power) { super(power); } void hear() { power.hearSubtleNoises(); } void smell() { power.trackBySmell(); } } class SuperHearSmell implements SuperHearing,SuperSmell { public void hearSubtleNoises() {} public void trackBySmell() {} } class DogBoy extends CanineHero<SuperHearSmell> { DogBoy() { super(new SuperHearSmell()); } } public class EpicBattle { // Bounds in generic methods: static <POWER extends SuperHearing> void useSuperHearing(SuperHero<POWER> hero) {//泛型方法 hero.getPower().hearSubtleNoises(); } static <POWER extends SuperHearing & SuperSmell> void superFind(SuperHero<POWER> hero) {//泛型方法 hero.getPower().hearSubtleNoises(); hero.getPower().trackBySmell(); } public static void main(String[] args) { DogBoy dogBoy = new DogBoy(); useSuperHearing(dogBoy); superFind(dogBoy); // You can do this: List<? extends SuperHearing> audioBoys; // But you can't do this: // List<? extends SuperHearing & SuperSmell> dogBoys; } } ///:~
other
The examples in this paper are all from Java programming ideas. The examples themselves are good, but the author explains them very little, so I add a detailed analysis to them. In fact, these examples need to be pondered over again. Only after intensive reading can we have a deep understanding of the generic extensions keyword.
The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.