目录
介绍泛型:
泛型实质上就是是程序员定义安全的类型,在没有出现泛型之前,Java也提供了对Object类型的引用“任意化”操作,这种“任意化”操作就是对Object类型引用进行向下转型及向上转型操作,但某些强制类型的转换错误也许不会被编译器捕获,而是在运行后出现异常,可见强制类型转化存在安全隐患,所以在此提供了泛型机制
回顾向上转型和向下转型
在介绍泛型之前,先来看一个例子,在项目中创建Test类,在该类中使基本类型向上转型为Object类型,具体代码如下:
public class Test {
private Object b;
public Object getB() {
return b;
}
public void setB(Object b) {
this.b = b;
}
public static void main(String[] args) {
Test test = new Test();
test.setB(Boolean.valueOf(true));//向上转型
System.out.println(test.getB());
test.setB(Float.valueOf("12.3"));
Float f=(Float) test.getB();//向下转型
System.out.println(f);
}
}
运行结果如下:
true
12.3
代码解释:
在本实例中,Test类中定义了私有的成员变量b,他的类型为Object类型,同时为其定义类相应的get和set方法,在main方法中,将Boolean.valueOf(true)作为setB()方法的参数,由于他的类型是Object,这样就完成了向上转型的操作,同时调用getB()方法,返回Object对象,这个是向下转型,问题通常出现在这里,因为向上转型是安全的,而如果进行向下转型的操作使用错了类型,就会出现异常,例如下面的代码:
test.setB(Float.valueOf("12.3"));
Integer f=(Integer) test.getB();
System.out.println(f);
这段并不存在语法错误,所以可以被编译器接收,但在执行时会出现ClassCastException异常,这样看来,向下转型操作通常会出现问题,而泛型机制有效的解决了这一问题。
定义泛型类:
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值和返回的值都以Object类为主,但是为了防止类型转换异常,Java提供泛型机制,其语法如下
类名<T>
其中,T是泛型的名字,代表某一种类型,开发者在创建该类对象时需要指定T所代表的时哪种具体的类型,T则采用Object类型
泛型的常规用法:
1.定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型,语法如下:
class MyClass<T1,T2>{ }
其中,T1和T2为可能被定义的类型
这样,在实例化指定类型的对象时就可以指定多个类型,类如:
MyClass<Boolean,Float> m = new MyClass();
2.定义泛型类时声明数组类型
定义泛型类时也可以声明数组类型,下面实例种定义泛型时便声明了数组类型。在项目中创建ArrayClass类,在该类中定义泛型声明数组类型
public class ArrayClass<T> {
private T[] array;
public T[] getArray() {
return array;
}
public void setArray(T[] array) {
this.array = array;
}
public static void main(String[] args) {
ArrayClass<String> demo = new ArrayClass<>();
String value[]={"成员1","成员2","成员3","成员4","成员5",};
demo.setArray(value);
String[] demoArray = demo.getArray();
for(int i=0;i<demoArray.length;i++){
System.out.println(demoArray[i]);
}
}
}
运行结果如下:
成员1
成员2
成员3
成员4
成员5
可见,可以在使用泛型机制时声明一个数组,但是不可以使用泛型来建立数组的实例,例如
3.集合类声明容器的元素
JDK中的集合接口,集合类都被定义了泛型,其中List<E>的泛型E实际上就是element元素的首字母,Map<K,V>就是key和value的首字母,常用的被泛型化的集合类如图所示
ArrayList | ArrayList<E> |
HashMap | HashMap<K,V> |
HashSet | HashSet<E> |
泛型的高级用法:
泛型的高级用法包括限制泛型可用类型和使用泛型通配符等
1.限制泛型可用类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例化做了限制,语法如下
class 类名<T extends anyclass>
其中anyclass指的是接口或者类
使用泛型限制后,泛型类的类型必须实现或继承anyclass这个接口或者类,无论anyclass时接口还是类,在进行泛型限制时都必须使用extends关键字
public class LimitClass <T extends List>{
public static void main(String[] args) {
//可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<>();
LimitClass<LinkedList> l2 = new LimitClass<>();
}
}
在上面的实例中,设置泛型类型必须实现List接口,当没有使用extends关键字限制泛型类型时,默认Object类下的所有子类都可以实例化泛型类对象,下面两个语句是等价的
2.使用类型通配符
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型对象时限制这个泛型的类型实现或继承某个接口或类的子类,要声明这样一个对象可以使用“ ?”通配符来表示,同时使用extends关键字来对泛型加以限制
泛型类名称<? extends List> a = null;
其中, <? extends List>表示类型未知,当需要使用泛型对象时,可以单独实例化,例如
A<? extends List> a = null;
a = new A<ArrayList>();
a = new A<LinkedList>();
如果实例化没有实现List接口的泛型对象,编译器会报错,例如实例化HashMap对象时,编译器会报错,因为HashMap类没有实现List接口
除了可以实例化一个限制泛型类型的实例,还可以将该实例放置在方法的参数中,例如:
public void doSomething(A<? extends List> a) { }
在上述代码中,定义方法有效的抑制了传入doSomething()方法的参数类型
如果使用A<?>这种形式实例化泛型类对象,则默认表示可以将A指定为实例化Object及以下的子类类型,例如:
List<String> l1 = new ArrayList<>();
l1.add("成员");
List<?> l2=l1;
List<?> l3=new LinkedList<Integer>();
System.out.println(l2.get(0));
上面例子中,List<?>类型的对象可以接受String类型的ArrayList集合,也可以接受Integer类型的LinkedList集合,注意:使用通配符声明的名称实例化的对象不能对其加入新的信息,只能获取或者删除
l1.set(0,"成员改变");
//l2.set(0,"成员改变"); //使用通配符的对象无法调用set方法
l2.get(0);
l2.remove(0);
//l2和l3通过使用通配符的方法创建起来,所有不能改变集合中的值
3.super关键字
泛型类型不但可以向下限制,也可以向上限制,使用super关键字,例如:
A<? super List> a = null
这样定义后,对象a只接受List接口或上层父类类型,如 a = new A<Object>();
4.继承泛型类与实现泛型接口
定义泛型的类和接口也可以被继承与实现,例如,让SubClass类继承ExtendClass类的泛型,代码如下:
public class ExtendClass <T1>{ }
class SubClass<T1,T2,T3> extends ExtendClass<T1>{ }
如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用“extends ExtendClass”语句进行继承操作,则SubClass类中的T1,T2,T3自动变为Object类型,所以一般情况下都保留父类的泛型类型
也可继承接口
interface SomeInterface<T1>{ }
class SubClass<T1,T2,T3> implements SomeInterface<T1>{}
泛型总结
1.泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种写法不对
2.泛型的类型个数可以是多个
3.可以使用extends和super关键字来限制泛型类型
4.可以使用通配符限制泛型的类型