每次创建一个集合,我们都需要指定这个集合里头能放什么元素,而放其他元素就会classcastexception;为了解决这个问题,我们可以创建类型为Object的集合,但是此时虽然我们什么类型都可以放了,但是正由于什么类型都能放,第一:元素会很乱;第二:用指定类型的变量接收集合返回的数据时,我们还需要进行强制类型转换,很麻烦.
泛型类❓- 语法:
class 类名 <泛型标识符,泛型标识符...>{
private 泛型标识 变量名;
...
}
常见的泛型标识:T/E/K/V
2:使用语法(利用泛型类实例化对象)
类名<具体的数据类型> 对象名=new 类名<具体的数据类型>(); //jdk1.7之后,后面的<>里的类型可省略不写
3:注意事项
- 泛型类在创建对象时,如果没有指定具体类型,将按Object类型来操作泛型类不支持基本数据类型(int char…)同一个泛型类,根据不同的数据类型所穿件的对象,本质是同一个类型
public class Test {
public static void main(String[] args) {
ArrayList arrayList1=new ArrayList<>();
ArrayList arrayList2=new ArrayList<>();
System.out.println(arrayList1.getClass() == arrayList2.getClass());//打印true
}
}
使用泛型类实现一个小案例
class ProductGetter{ Random random=new Random(); //奖品 private T product; //奖池 private ArrayList list=new ArrayList<>(); //添加奖品 public void addProduct(T t){ list.add(t); } //抽奖 public T getProduct(){ int index=random.nextInt(list.size()); return list.get(index); } } public class Test { public static void main(String[] args) { ProductGetter gift=new ProductGetter<>(); String[] gifts={"苹果手机","小米手环","扫地机器人","500元现金"}; //为奖池添加奖品 for(int i=0;i<4;i++){ gift.addProduct(gifts[i]); } //开始抽奖 String ret=gift.getProduct(); System.out.println(ret); } }
- 实例化泛型类时,将类型作为了参数,即类型参数化从该集合中获取到的元素,不需要像Object集合那样,每次都要强转,这里编译器可自动为我们完成强转
泛型类派生子类
子类也是泛型类,子类和父类的泛型类型要一致;即使子类的泛型标识有多个,也要保证子类的泛型标识中至少有一个和父类的泛型标识一致
class childextends parent
子类不是泛型类,那此时该子类继承一个泛型类时,就要明确想继承什么类型的父类了
class child extends parent
接口注定是被实现类去实现的,所以实现类具体是不是泛型类,并且有什么要求,和泛型类派生子类是一样的规定
语法
interface 接口名称 <泛型标识符,泛型标识符...>{
泛型标识 方法名();//自带public abstarct 修饰的抽象方法
}
具体
interface Generator{ T getKey(); } class Apple implements Generator { //重写的方法,从泛型集合中获取不需要强转,说明类型就是我们所指定的String,所以为了 //形成重写,这里的返回值类型应当写成我们所指定的类型 public String getKey(){ return "hello generic"; } }
interface Parent{ T getKey(); } //保证至少一个相同,是因为构造子类对象时,类型参数的其中若干个也可以为泛型父类提供类型参数信息,进而构造出具体的父类 class child implements Parent { private T key; private E value; public child(T key, E value) { this.key = key; this.value = value; } @Override public T getKey() { return key; } public E getValue(){ return value; } }
泛型方法⚡️
在调用方法的时候,才会指明这个方法带有的泛型标识代表的具体是什么类型.
语法:
修饰符 <泛型标识,泛型标识...> 返回值类型 方法名(形参列表){
方法体;
}
必须要求有<泛型标识>存在,否则不叫泛型方法,要区别于前述泛型类里定义的带有泛型标识的方法,但是没带<>,所以它们都不是泛型方法.
泛型标识也可以随便写,比如:T/E/V/K等
使用
class ProductGetter{ Random random=new Random(); //奖品 private T product; //奖池 private ArrayList list=new ArrayList<>(); //添加奖品 public void addProduct(T t){ list.add(t); } //抽奖 //泛型方法 public E getProduct(ArrayList list){ int index=random.nextInt(list.size()); return list.get(index); } } public class Test { public static void main(String[] args) { ArrayList list1=new ArrayList<>(); ArrayList list2=new ArrayList<>(); list1.add("苹果手机"); list1.add("扫地机器人"); list1.add("小米手环"); list1.add("500元现金"); list2.add(1000); list2.add(2000); list2.add(3000); ProductGetter gift=new ProductGetter<>(); String product = gift.getProduct(list1); System.out.println(product); System.out.println(gift.getProduct(list2)); } }
可以看出,使用泛型方法,就不用先填充奖池,再抽奖了,其次可以发现,泛型方法使用的类型可以独立于泛型类的指定类型.还要知道,普通类里也能写泛型方法!
注意事项
- 只是使用了泛型标识的普通方法,不能被static修饰泛型方法可以被static修饰泛型方法可以定义在普通类中可以了解一下可变参数的泛型方法的使用
类型通配符❎
- 类型通配符一般用 ? 表示类型通配符代表的是类型实参,不是类型形参思考一个问题:考虑为什么box2传参已经编译出错了!?
class Box{ private T first; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } } public class Test2 { public static void main(String[] args) { Box box1=new Box<>(); box1.setFirst(100); showBox(box1); Box box2=new Box<>(); box2.setFirst(200); showBox(box2); } private static void showBox(Box box){ Number first=box.getFirst(); System.out.println(first); } }
答:box2和showBox的形参类型不匹配.我们一开始的思想可能想用向上转型去理解,但是这里并不构成父子关系.那如何改进,看下面这个方法可行?
class Box{ private T first; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } } public class Test2 { public static void main(String[] args) { Box box1=new Box<>(); box1.setFirst(100); showBox(box1); Box box2=new Box<>(); box2.setFirst(200); showBox(box2); } private static void showBox(Box box){ Number first=box.getFirst(); System.out.println(first); } private static void showBox(Box box){ Number first=box.getFirst(); System.out.println(first); } }
答:改成上述写法,可能我们的想法是既然向上转型不可行,那我利用方法的重载总可以了吧!?结果是否定的,重载的要求是:返回值类型相同,函数名相同,形参列表不同,但是,这里的形参列表本质是相同的,所以并没有构成方法的重载.那往下引出类型通配符
class Box{ private T first; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } } public class Test2 { public static void main(String[] args) { Box box1=new Box<>(); box1.setFirst(100); showBox(box1); Box box2=new Box<>(); box2.setFirst(200); showBox(box2); } private static void showBox(Box> box){ Object first=box.getFirst(); System.out.println(first); } }
这时可正常打印了;也就是说:指定了类型的泛型类类型作为形参,实参必须是与形参一致;其次通配符的出现,可允许实参被指定各种类型.
通配符的上限
类/接口 extends 实参类型>
要求该泛型的类型,只能是实参类型或者实参类型的子类类型.
所以可以将上述代码改成:
class Box通配符的下限Ⓜ️{ private T first; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } } public class Test2 { public static void main(String[] args) { Box box1=new Box<>(); box1.setFirst(100); showBox(box1); Box box2=new Box<>(); box2.setFirst(200); showBox(box2); } private static void showBox(Box extends Number> box){ Number first=box.getFirst();//Number作为了上限,用这个类型接收,即使向上转型也没关系 System.out.println(first); } }
类/接口 super 实参类型>
要求该泛型的类型,只能是实参类型,或者实参类型的父类类型
所以上述代码可以改成:
class Box{ private T first; public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } } public class Test2 { public static void main(String[] args) { Box box1=new Box<>(); box1.setFirst(100); showBox(box1); Box box2=new Box<>(); box2.setFirst(200); showBox(box2); } private static void showBox(Box super Integer> box){ Object first=box.getFirst();//上限不定,只能用Object接收了 System.out.println(first); } }
类型擦除✌️
jdk1.5引入的,泛型代码能够很好的与之前的版本代码兼容,这是因为泛型信息只是存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,称类型擦除.
无限制的类型擦除
public class Erasure{ private T key; public T getKey(){ return key; } public void setKey(T key){ this.key=key; } } //擦除完: public class Erasure{ private Object key; public Object getKey(){ return key; } public void setKey(Object key){ this.key=key; } }
有限制的类型擦除
就是说泛型类带了上限,此时就是把上面的Object擦成上限类型即可.
泛型方法的类型擦除
与泛型类擦除方法一致.
- 可以申明带泛型的数组引用,但不能直接实例化一个泛型数组可以通过java.lang.reflect.Array的newInstance(Class ,int)来创建T[]数组后续介绍反射时,再重点介绍泛型数组的构建



