文章目录
泛型
01集合中的泛型
1.1 泛型的优势
- 明确ArraylIst存储的对象类型,避免强制转换对象类型,同是避免遗忘对象所属类型;
- 泛型的用法
<Type>
,Type可以是接口,类,抽象类; - 只能存放该Type,其他放不进去。
- 可以存放Type的子类。
1.2 泛型的简写
ArrayList<Hero> heros2 = new ArrayList<>();
后面的泛型用<>代替,可写可不写。
补充作业:
若要设计一个集合,既可以放整数,也可以放浮点数,但不能放字符串。
需要使用到NUmber类。
ArrayList<Number> number = new ArrayList<>();
02 支持泛型的类
2.1 不支持泛型类
以Stack栈为例子,如果不支持泛型
当需要一个只能放Hero的栈的时候,就需要设计一个HeroStack;
当需要一个只能放Item的栈的时候,就需要一个ItemStack;
package generic;
//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);
}
}
}
package generic;
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);
}
}
}
2.2 支持泛型的类
设计一个支持泛型的栈MyStack
设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
package generic;
import java.util.HashMap;
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.addLast(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
heroStack.push(new Item());
//在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
//不能放Hero
itemStack.push(new Hero());
}
}
练习-支持泛型的二叉树
package collection;
import java.util.ArrayList;
import java.util.List;
/*
*@Author hmg
*@Description
*@Time 2019年12月9日下午8:12:08
*@Version1.0
*/
public class Node<T>{
public Node<T> leftNode;
public Node<T> rightNode;
public T value;
public void add(T v) {
if(null==value) {
value=v;
}
else{
if((Integer)v-(Integer)value<=0) {
if(null==leftNode)
leftNode=new Node<T>();
leftNode.add(v);
}
else {
if(null==rightNode)
rightNode=new Node<T>();
rightNode.add(v);
}
}
}
public List<T> values(){
List<T> values=new ArrayList<>();
if(null!=leftNode) {
values.addAll(leftNode.values());
}
values.add(value);
if(null!=rightNode) {
values.addAll(rightNode.values());
}
return values;
}
public static void main(String[] args) {
int [] list=new int[] {65,23,67,8,13,456,78,43,56,102};
Node<Integer> roots=new Node<>();
for(int i:list) {
roots.add(i);
}
System.out.println(roots.values());
}
}
问题:
但是这样的泛型有个弊端,在比较的时候,泛型T被转换为了Integer进行大小比较。换句话说,如果写成非Integer泛型,就会出现类型转换异常。
这个问题的解决,需要用到后面的 ? extends 模式
03 通配符
3.1 ? extends
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());
ArrayList<? extends Hero> heroList = apHeroList;
ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型
- heroList 的泛型可能是Hero
- heroList 的泛型可能是APHero
- heroList 的泛型可能是ADHero
所以 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
但是,不能往里面放东西,因为
放APHero就不满足< ADHero >
放ADHero又不满足< APHero >
3.2 ? super
ArrayList<? super Hero> heroList = new ArrayList<Object>();
ArrayList heroList<? super Hero> 表示这是一个Hero泛型或者其父类泛型
- heroList的泛型可能是Hero
- heroList的泛型可能是Object
可以往里面插入Hero以及Hero的子类
但是取出来有风险,因为不确定取出来是Hero还是Object(Object是强转Hero会失败)。
3.3 泛型通配符?
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
//?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;
泛型通配符? 代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能
所以只能以Object的形式取出来
并且不能往里面放对象,因为不知道到底是一个什么泛型的容器
3.4 总结
- 如果希望只取出,不插入,就使用? extends Hero
- 如果希望只插入,不取出,就使用? super Hero
- 如果希望,又能插入,又能取出,就不要用通配符?
3.5 练习- ?extends
减肥前:
为了遍历不同泛型的3种集合,需要设计3个方法
package generic;
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
public class TestGeneric {
public static void iterate(ArrayList<Hero> list) {
for (Hero hero : list) {
System.out.println(hero.name);
}
}
public static void iterateAP(ArrayList<APHero> list) {
for (Hero hero : list) {
System.out.println(hero.name);
}
}
public static void iterateAD(ArrayList<ADHero> list) {
for (Hero hero : list) {
System.out.println(hero.name);
}
}
public static void main(String[] args) {
ArrayList<Hero> hs = new ArrayList<>();
ArrayList<APHero> aphs = new ArrayList<>();
ArrayList<ADHero> adhs = new ArrayList<>();
iterate(hs);
iterateAP(aphs);
iterateAD(adhs);
}
}
减肥后:
只要一个方法
package generic;
import java.util.ArrayList;
import charactor.ADHero;
import charactor.APHero;
import charactor.Hero;
public class TestGeneric {
public static void iterate(ArrayList<? extends Hero> list) {
for (Hero hero : list) {
System.out.println(hero.name);
}
}
public static void main(String[] args) {
ArrayList<Hero> hs = new ArrayList<>();
ArrayList<APHero> aphs = new ArrayList<>();
ArrayList<ADHero> adhs = new ArrayList<>();
iterate(hs);
iterate(aphs);
iterate(adhs);
}
}
3.6 练习- 二叉树
package collection;
import java.util.ArrayList;
import java.util.List;
import charactor.GiantDragon;
import charactor.Hero;
//把Node设计为
public class Node<T extends Comparable<T>> {
//表示只能使用那些实现了Comparable接口的泛型,比如Integer,Hero
public Node<T> leftNode;
public Node<T> rightNode;
public T value;
public void add(T t) {
if (null == value)
value = t;
else {
//t和value都是T 类型,而T类型extends Comparable,所以必然提供compare接口
if (t.compareTo(value) <= 0) {
if (null == leftNode)
leftNode = new Node<T>();
leftNode.add(t);
}
else {
if (null == rightNode)
rightNode = new Node<T>();
rightNode.add(t);
}
}
}
public List<T> values() {
List<T> values = new ArrayList<>();
if (null != leftNode)
values.addAll(leftNode.values());
values.add(value);
if (null != rightNode)
values.addAll(rightNode.values());
return values;
}
public static void main(String[] args) {
int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
Node<Integer> roots = new Node<>();
for (int number : randoms) {
roots.add(number);
}
System.out.println(roots.values());
//Integer实现了 Comparable接口,所以可以作为Node的泛型
//Hero实现了 Comparable接口,所以可以作为Node的泛型
Node<Hero> heros = new Node<>();
//GiantDragon 没有实现 Comparable接口,所以不能作为Node的泛型
Node<GiantDragon> dragons = new Node<>();
}
}
04 泛型转型
4.1 子类转父类?
ArrayList<Hero> hs =new ArrayList<>();
ArrayList<ADHero> adhs =new ArrayList<>();
//子类泛型转父类泛型
hs = adhs;//?
假设可以转型成功
引用hs指向了ADHero泛型的容器
作为Hero泛型的引用hs, 看上去是可以往里面加一个APHero的。
但是hs这个引用,实际上是指向的一个ADHero泛型的容器
如果能加进去,就变成了ADHero泛型的容器里放进了APHero,这就矛盾了
所以子类泛型不可以转换为父类泛型。
4.1 父类转子类?
ArrayList<Hero> hs =new ArrayList<>();
ArrayList<ADHero> adhs =new ArrayList<>();
//子类泛型转父类泛型
adhs = hs;//?
假设能成功
这个时候adhs实际上指向的是泛型是Hero的容器,而这个容器里可能放的是一个APHero
而根据泛型,直接取出来就转型成了ADHero
所以就变成了APHero转型成ADHero,这是矛盾的。
所以反推,父类泛型不能转型为子类泛型。
本文学习内容均来自how2j.cn,用于个人笔记和总结,想学习的可以到该网站学习哦,侵删。