java基础 ---- 泛型
泛型的理解概述:
- 泛型是一种未知数据类型,可视为一种变量,用来接收具体的数据类型。
- 在设计API时可以指定类,方法,接口支持泛型,那么使用API时会变得简洁,并在编译时期的得到语法检查。比如: 集合API源码申明了泛型,在使用集合API时确定泛型 “存什么,就取什么”清晰命明了,并且规避了将所有元素强制转型成单一类型可能出现的ClassCastException执行错误(见测试代码I)。
泛型的使用概述:
泛型可应用到的类、方法、接口中,将数据类型作为参数进行传递。
- 定义类时,在类名后声明泛型,成员变量可使用泛型;使用类时,创建对象的同时确定泛型数据类型,类中所有用到泛型的地方随之确定。
- 定义方法时,在方法的修饰符和返回值类型之间声明泛型,方法参数可使用泛型;使用方法时确定泛型,传入什么类型参数就是什么泛型。
- 定义接口时,在接口名后声明泛型,方法参数,方法返回值可使用泛型;使用接口时,第一种方法是定义实现类的同时确定接口泛型,第二种方法是定义实现类的同时声明类含有泛型,接口与实现类的泛型一致,在创建对象的时候确定泛型数据类型。
集合中泛型通配符,泛型限定的测试代码:
/**运行前提:
*1.IDEA提示下导入更多的包
*/
public class MyTest {
public static void main(String[] args) {
show1();
show2();
show3();
}
/** show1():不使用泛型与使用泛型对比
* 分别创建不使用泛型,使用泛型的集合对象
* 测试分别为两个集合添加元素
* 分别为两个集合对象创建迭代器
* 测试分别获取两个集合的元素
* 测试分别获取两个集合字符串元素的字符数
*/
public static void show1() {
//1.
//集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
Iterator it = list.iterator();
while (it.hasNext()) {
//
//取出元素也是Object类型
Object obj = it.next();
System.out.println(obj);
//
//使用String类特有的方法,length获取字符串的长度,需要向下转型
String b = (String) obj; //会抛出ClassCastException类型转换异常,不能把Integer类型数据1转换为String类型
System.out.println(b.length());
}
//2.
//集合使用泛型,泛型是什么类型就只能存储什么类型的数据
//JDK1.7版本之后,=号后边的泛型可以省略,后边的泛型可以根据前边的泛型推导出来
ArrayList<String> list2 = new ArrayList<>();
list2.add("abc");
//list.add(1); 错于存储与泛型不符的数据类型
//
//迭代器的泛型与集合保持一致
Iterator<String> it2 = list2.iterator();
while (it2.hasNext()) {
//
//取出元素也是String类型,所以可以直接调用length()方法
String a = it2.next();
System.out.println(a.length());
}
}
/**show2()调用的方法:
*定义一个能遍历所有泛型集合的方法
*/
public static void printArray(ArrayList<?> list){//?代表任意泛型类型
// public static void printArray(ArrayList<String> list)只能遍历<String>集合
// public static void printArray(ArrayList<Object> list)错于泛型没有继承概念
// public static void printArray(ArrayList list)
//
Iterator<?> it = list.iterator();
while(it.hasNext()){
//it.next()方法,取出的元素是Object,可以接收任意的数据类型
Object o = it.next();
System.out.println(o);
}
}
/**show2():泛型通配符
* 分别创建使用两种泛型的集合对象,并为集合添加元素
* 测试调用方法遍历集合
*/
public static void show2() {
ArrayList<Integer> list01 = new ArrayList<>();
ArrayList<?> list01 = new ArrayList<?>();//错于集合泛型只能集合作方法参数时使用,不能建立集合对象时使用
list01.add(1);
list01.add(2);
ArrayList<String> list02 = new ArrayList<>();
list02.add("a");
list02.add("b");
printArray(list01);
printArray(list02);
}
/**show3()调用的方法:
*分别用泛型上限,泛型下限定义方法
*/
//参数含有泛型上下限定格式: 类名<? extends/super 上限泛型/下限泛型>对象名
public static void getElement1(Collection<? extends Number> coll){}
public static void getElement2(Collection<? super Number> coll){}
/**show3():泛型限定
*分别创建四种有继承关系的泛型集合对象
* 测试四类对象分别调用两种泛型限定方法
*/
public static void show3(){
/*泛型继承关系:Integer extends Number extends Object
String extends Object*/
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
//
//getElement1参数泛型上限为Number,只能接受Number及其子类
getElement1(list1);
getElement1(list3);
//getElement1(list2);错于String类和Number没有继承关系
//getElement1(list4);错于Object类是Number的父类
//
//getElement1参数泛型下限为Number,只能接受Number及其父类
getElement2(list3);
getElement2(list4);
//getElement2(list1);错于Integer类是Number类的下限
//getElement2(list2);错于String类和Number类没有继承关系
}
}
类,方法,接口声明泛型,使用泛型,确定泛型的测试代码:
/**
* 定义一个泛型类,成员变量使用泛型
*/
class GenericClass<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
/**
* 定义一个类,包含泛型方法,方法参数使用泛型
*/
class GenericMethod {
//定义一个含有泛型的方法
public <M> void method01(M m) {
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s) {
System.out.println(s);
}
}
/**
* 定义一个泛型接口,包含泛型方法,方法参数使用泛型
*/
interface GenericInterface<I> {
public abstract void method(I i);
}
public class MyTest {
public static void main(String[] args) {
show1();
show2();
show3();
}
/**
* show1():测试含有泛型的类
* 分别创建默认泛型为Object类型对象,泛型为Integer对象,String对象
* 测试调用成员方法
*/
public static void show1() {
//不写泛型默认为Object类型
GenericClass gc = new GenericClass();
gc.setName(1);
Object obj = gc.getName();
//创建GenericClass对象,泛型使用Integer类型
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setName(1);
Integer name = gc2.getName();
System.out.println(name);
//创建GenericClass对象,泛型使用String类型
GenericClass<String> gc3 = new GenericClass<>();
gc3.setName("小明");
String name1 = gc3.getName();
System.out.println(name1);
}
/**
* show2():测试含有泛型的方法
* 创建类的对象
* 调用含有泛型的一般方法,含有泛型的静态方法
*/
public static void show2() {
//创建GenericMethod对象
GenericMethod gm = new GenericMethod();
//传递什么类型的参数,就是确定的什么泛型
gm.method01(10);
//gm.method01("abc");
//gm.method01(8.8);
//gm.method01(true);
//静态方法,通过类名.方法名(参数)可以直接使用
GenericMethod.method02("静态方法");
//GenericMethod.method02(1);
//gm.method02("静态方法"); 静态方法不建议创建对象使用
}
/**
* show3():含有泛型接口的两种实现方式
* 1.分别用两种方式定义接口实现类作MyTest内部类
* 2.创建实现类对象
* 调用重写方法
*/
public static void show3() {
//1.
//定义一个类实现接口,并确定接口
class GenericInterfaceImpl1 implements GenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
//
//定义一个泛型类实现接口,不确定接口
class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
//
//类的泛型与方法的泛型独立
public <S> void method2(S s) {
}
}
//2.
//第一种方式的实现类,使用时就不需要在声明
GenericInterface gi = new GenericInterfaceImpl1();
gi.method("AAA"); //AAA
//
//第二种方式的实现类,创建对象的同时声明接口泛型
GenericInterface<Integer> GI = new GenericInterfaceImpl2<>();
GI.method(1); //1
GI.method2("abc"); //abc
}
}