1)泛型是什么:
是一种未知的数据类型,当我们不知道使用什么数据类型时,可以使用泛型。
泛型也可以看作是一个变量,用来接收数据类型。
E e:Element 元素
T t:Type 类型
2)使用泛型解决的问题:
解决向下转型带来的类转换异常(ClassCastException)。
3)使用泛型的好处:
●避免了类转换的麻烦,存储的是什么类型,取出的就是什么类型。
●把运行期异常提升到了编译期异常。
弊端:不使用泛型时能存储任意类型的数据。(不适用默认则为Object)
public class Test {
public static void main(String[] args) {
// show1();
show2();
}
//不使用泛型
public static void show1() {
//数组不适用泛型则为默认类型,为Object
ArrayList list = new ArrayList();
list.add(1);
list.add("abc");
//迭代输出
Iterator it = list.iterator();
while (it.hasNext()){
Object obj = it.next();
System.out.println(obj);
//调用String特有的方法,Length获取长度
//向下转型,会抛出异常,不能把Integer类型转换为String类型
String s =(String) obj ;
System.out.println(s.length());
}
}
//使用泛型
public static void show2(){
ArrayList<String> list =new ArrayList<>();
list.add("Aaa");
list.add("Bbb");
list.add("Ccc");
//list.add(123); 报错
//迭代输出
for (String str : list){
System.out.println(str);
}
}
}
4)含有泛型的类:
使用泛型后,所有类中属性的类型都是动态设置。即创建对象时是什么类型,泛型就是什么类型。
//含有泛型的类
public class GenericClass <E> {
public E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
public class doMain {
public static void main(String[] args) {
GenericClass<String> gc1 = new GenericClass<>();
gc1.setName("String类型");
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setName(1);
}
}
如果不使用泛型,我们就只能使用一种数据类型。(Object除外,使用Object则会带来安全隐患问题)
5)含有泛型的方法:
定义在方法的修饰符和返回值之间。
作用:调用方法时,传递什么类型,泛型就是什么类型。
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
方法体;
}
public class GenericMethod {
//含有泛型的方法
public <T> void method(T t){
System.out.println(t);
}
//含有泛型的静态方法
public static <S> void method2(S s){
System.out.println(s);
}
}
public class doMain {
public static void main(String[] args) {
GenericMethod gm = new GenericMethod();
gm.method(10);
gm.method("abc");
gm.method(13);
GenericMethod.method2("静态方法");
GenericMethod.method2("2");
}
}
6)含有泛型的接口:
//含有泛型的接口
interface IGeneric<I> {
public abstract void method(I i);
}
实现方式一:
实现类继续设置泛型标记。
class IGenericImpl<I> implements IGeneric <I>{
@Override
public void method(I i) {
System.out.println(i);
}
}
实现方式二:
class IGenericImpl2 implements IGeneric<String>{
@Override
public void method(String s) {
System.out.println(s);
}
}
一和二的差别:
有没有为子类设置具体的泛型。
public class doMain {
public static void main(String[] args) {
IGenericImpl gil = new IGenericImpl();
gil.method("使用泛型接口的实现类1");
gil.method(1);
IGenericImpl2 gil2 = new IGenericImpl2();
gil2.method("使用泛型接口的实现类2");
}
}
7)泛型的通配符:
<?>解决的是参数传递的问题。
它只能够接收数据,不能够存储数据。
public class Generic {
public static void main(String[] args) {
ArrayList<Integer> list01 =new ArrayList<>();
list01.add(1);
list01.add(2);
ArrayList<String> list02 = new ArrayList<>();
list02.add("a");
list02.add("b");
printArray(list01);
printArray(list02);
//它只能够接收数据,不能够存储数据
//ArrayList<?> list03 = new ArrayList<?>();报错
}
//定义一个方法,能遍历所有类型的ArrayList集合。
//这时候我们不知道ArrayList集合使用什么数据类型,可以用泛型的通配符 ? 来接收数据类型
// public static void printArray(ArrayList<String> list){
public static void printArray(ArrayList<?> list){
Iterator<?> it = list.iterator();
while (it.hasNext()){
Object obj =it.next();
System.out.println(obj);
}
}
}
注意:在明确设置一个类为泛型类型时,没有继承关系的概念。
public static void printArray(ArrayList<String> list){
public static void printArray(ArrayList<Object> list){
虽然Object与String类是父子类的关系,但换到泛型中就属于两个完全独立的概念。
通配符的高级使用——受限泛型
●泛型的上限:
格式:类名 <? extends 类 > 对象名,可以在方法声明和参数上使用。
作用:只能够接收该类型及其子类。
●泛型的下限:
格式:类名 <? super 类 > 对象名,可以在方法参数上使用。
作用:只能够接收该类型及其父类。
public class doMain {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
Collection<Number> list3 = new ArrayList<>();
Collection<Object> list4 = new ArrayList<>();
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
//泛型的上限:此时的泛型?,必须是Number类或其类型的子类
public static void getElement1(Collection<? extends Number> coll){}
//泛型的下限:此时的泛型?,必须是Number类或其类型的父类
public static void getElement2(Collection<? super Number> coll){}
}