1. 集合中的泛型
不使用泛型带来的问题:
- ADHero(物理攻击英雄) APHero(魔法攻击英雄)都是Hero的子类。
- ArrayList默认接受Object类型的对象,所以所有对象都可以放进ArrayList中。所以get(0)返回的类型是Object。
- 接着,需要进行强制转换才可以得到APHero类型或者ADHero类型。
如果软件开发人员记忆比较好,能记得哪个是哪个,还是可以的。但是开发人员会犯错误,会记错,容易出现类型转换异常。
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
public class TestGeneric {
public static void main(String[] args) {
ArrayList heros = new ArrayList();
heros.add(new APHero());
heros.add(new ADHero());
APHero apHero = (APHero) heros.get(0);
ADHero adHero = (ADHero) heros.get(1);
// java.lang.ClassCastException: charactor.APHero cannot be cast to charactor.ADHero
// ADHero adHero2 = (ADHero) heros.get(0);
}
}
使用泛型
使用泛型的好处:
- 泛型的用法是在容器后面添加<Type>
- Type可以是类,抽象类,接口
- 泛型表示这种容器,只能存放APHero,ADHero就放不进去了。
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
public class TestGeneric {
public static void main(String[] args) {
ArrayList<APHero> heros = new ArrayList<APHero>();
// 只有APHero可以放进去
heros.add(new APHero());
// ADHero甚至放不进去
// The method add(APHero) in the type ArrayList<APHero> is not applicable for the arguments (ADHero)
// heros.add(new ADHero());
// 获取的时候也不需要进行转型,因为取出来的一定是APHero
APHero apHero = heros.get(0);
}
}
子类对象
假设容器的泛型是Hero,那么Hero的子类 APHero,ADHero 都可以放进去。
不过,和Hero无关的类型Item还是放不进去。
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
import property.Item;
public class TestGeneric {
public static void main(String[] args) {
ArrayList<Hero> heros = new ArrayList<Hero>();
// 只有APHero可以放进去
heros.add(new APHero());
heros.add(new ADHero());
// 和Hero无关的类型Item还是放不进去
// The method add(Hero) in the type ArrayList<Hero> is not applicable for the arguments (Item)
// heros.add(new Item());
}
}
2. 支持泛型的类
以Stack栈为例,如果不使用泛型:
- 当需要一个只能放Hero的栈的时候,就需要设计一个HeroStack
- 当需要一个只能放Item的栈的时候,就需要设计一个ItemStack
HeroStack类:
import java.util.LinkedList;
import charactor.Hero;
public class HeroStack {
LinkedList<Hero> heros = new LinkedList<Hero>();
public void push(Hero h) {
heros.addLast(h);
}
public Hero pull() {
return heros.removeLast();
}
public Hero peek() {
return heros.getLast();
}
public static void main(String[] args) {
HeroStack heroStack = new HeroStack();
for (int i = 0; i < 5; i++) {
Hero h = new Hero("hero name " + i);
System.out.println("压入 hero:" + h);
heroStack.push(h);
}
for (int i = 0; i < 5; i++) {
Hero h =heroStack.pull();
System.out.println("弹出 hero" + h);
}
}
}
ItemStack类:
import java.util.LinkedList;
import property.Item;
public class ItemStack {
LinkedList<Item> Items = new LinkedList<Item>();
public void push(Item h) {
Items.addLast(h);
}
public Item pull() {
return Items.removeLast();
}
public Item peek() {
return Items.getLast();
}
public static void main(String[] args) {
ItemStack ItemStack = new ItemStack();
for (int i = 0; i < 5; i++) {
Item item = new Item("Item name " + i);
System.out.println("压入 Item:" + item);
ItemStack.push(item);
}
for (int i = 0; i < 5; i++) {
Item item = ItemStack.pull();
System.out.println("弹出 Item" + item);
}
}
}
支持泛型的Stack:
- 设计一个支持泛型的栈MyStack
- 设计这个类的时候,在类的声明上,加上一个<T>,表示该类型支持泛型。
- T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但一般约定俗成使用T,代表类型。
import java.util.LinkedList;
import charactor.Hero;
import property.Item;
public class MyStack <T> {
LinkedList<T> values = new LinkedList<T>();
public void push(T t) {
values.add(t);
}
public T pull() {
return values.removeLast();
}
public T peek() {
return values.getLast();
}
public static void main(String[] args) {
// 在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
MyStack<Hero> heroStack = new MyStack<>();
heroStack.push(new Hero());
// 不能放Item
// The method push(Hero) in the type MyStack<Hero> is not applicable for the arguments
// heroStack.push(new Item());
// 在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
// 不能放Hero
// The method push(Item) in the type MyStack<Item> is not applicable for the arguments (Hero)
// itemStack.push(new Hero());
}
}
3. 通配符
? extends
ArrayList<? extends Hero> heroList 表示这是一个Hero泛型或者其子类泛型
- heroList 的泛型可能是Hero
- heroList 的泛型可能是APHero
- heroList 的泛型可能是ADHero
所以,可以确凿的是,从heroList取出来的对象那个,一定是可以转型成Hero的。
但是,不能往里面放东西,因为:
- 放APHero就不满足<ADHero>
- 放ADHero又不满足<APHero>
import java.util.ArrayList;
import charactor.APHero;
import charactor.Hero;
public class TestGeneric {
public static void main(String[] args) {
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());
ArrayList<? extends Hero> heroList = apHeroList;
// ? extends Hero 表示这是一个Hero泛型的子类泛型
// heroList 的泛型可以是Hero
// heroList 的泛型可以是APHero
// heroList 的泛型可以是ADHero
// 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
Hero h = heroList.get(0);
// 但是,不能往里面放东西
// heroList.add(new ADHero()); // 编译错误,因为heroList的泛型,有可能是APHero
}
}
? super
ArrayList<? super Hero> heroList 表示这是一个Hero泛型或者其父类泛型。
- heroList的泛型可能是Hero
- heroList的泛型可能是Object
可以往里面插入Hero以及Hero的子类
但取出来有风险,因为不确定取出来的是Hero还是Object。
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
public class TestGeneric {
public static void main(String[] args) {
ArrayList<? super Hero> heroList = new ArrayList<Object>();
// ?super Hero 表示heroList的泛型是Hero或者其父类泛型
// heroList 的泛型可以是Hero
// heroList 的泛型可以是Object
// 所以就可以插入Hero
heroList.add(new Hero());
// 也可以插入Hero的子类
heroList.add(new APHero());
heroList.add(new ADHero());
// 但是,不能从里面取数据出来,因为其泛型可能是Object,而Object强转Hero会失败
// Hero h = heroList.get(0);
// 但是,加强转符号会可以
Hero h = (Hero) heroList.get(0);
}
}
泛型通配符 ?
泛型通配符 ? 代表任意类型。
既然 ? 代表任意泛型,那么换句话说,这个容器什么泛型都有可能。所以只能以Object的形式取出来,并且不能往里面放对象,因为不知道到底是一个什么泛型的容器。
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
public class TestGeneric {
public static void main(String[] args) {
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
// ?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;
// ?的缺陷1:既然?代表任意类型,那么换句话说,你就是不知道这个容器里是什么类型
// 所以只能以Object的形式取出来
Object o = generalList.get(0);
// ?的缺陷2:既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
// 所以,放哪种对象进去,都有风险,结果就是什么类型的对象,都不能放进去
generalList.add(new Item()); // 编译错误 因为?代表任意泛型,很有可能不是Item
generalList.add(new Hero()); // 编译错误 因为?代表任意泛型,很有可能不是Hero
generalList.add(new APHero()); // 编译错误 因为?代表任意泛型,很有可能不是APHero
}
}
总结:
如果希望只取出,不插入,就使用 ? extends Hero
如果希望只插入,不取出,就使用 ? super Hero
如果希望,又能插入,又能取出,就不要用通配符。