一、Java泛型入门基础
1、 泛型历史:集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,需要进行向下转型,如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常。这样就给程序带来了不安全性。
在jdk1.5以后就有了解决方案——泛型技术:在存储元素时,就不允许存储不同类型的元素。存储了就编译失败。 所以就需要在存储元素时,在容器上明确具体的元素类型,这其实和数组定义很像。
2、优势:1)将运行时期的ClassCastException异常转移到了编译时期,进行检查,并以编译失败来体现。 这样有利于程序员尽早解决问题。
2)避免了向下转型(强转)的麻烦。
3、在什么情况下使用泛型呢?
只要在使用类或者接口时,该类或者接口在api文当描述时都带着<>,就需要在使用时定义泛型。
其实,泛型无非就是通过<>定义了一个形式参数,专门用于接收具体的引用类型。在使用时,一定要传递对应的实际参数类型。
集合中泛型的应用特别多见。
二、Java泛型使用
使用泛型的动机举例(以集合为例):
class MySet:(自行写的MySet,实现的Java泛型)
public class MySet {
private Object[] objs=new Object[0];
public boolean add(E obj){
if(contains(obj)){
return false;
}
Object tempObjs[] = new Object[objs.length+1];
System.arraycopy(objs, 0, tempObjs, 0, objs.length);
tempObjs[objs.length] = obj;
objs = tempObjs;
return true;
}
public boolean contains(E obj){
for(Object o:objs){
if(o.equals(obj)){
return true;
}
}
return false;
}
public Object[] getAll(){
return objs;
}
public int size(){
return objs.length;
}
}
测试class:
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo2 {
public static void main(String[] args) {
// MySet set = new MySet();
// set.add("abcd");
// set.add(8);//编译出错
// MySet set = new MySet();
// set.add("abcd");//编译出错
// set.add(8);
}
}
三、Java泛型类
1、概念:当一个类要操作的引用数据类型不确定的时候,可以将该类型定义一个形参。用到的这类时,由使用者来通过传递类型参数的形式,来确定要操作的具体的对象类型。这意味着在定义这个类时,需要在类上定义形参,用于接收具体的类型实参。这就是将泛型定义在类上,即泛型类。
2、什么时候使用泛型类呢?
只要类中操作的引用数据类型不确定的时候,就可以定义泛型类。 有了泛型类,省去了曾经的强转和类型转换异常的麻烦。
代码示例:
/*
* 泛型类的演示
*/
public class GenericDemo2 {
public static void main(String[] args) {
MyVessel u = new MyVessel();
//u.setObject(new Student());//不行,u中存放的是Worker,实参只能是Worker类型
u.setObject(new Worker());
Worker w = u.getObject();
System.out.println(w);
MyVessel v = new MyVessel();
//v.setObject(new Worker());//不行,实参只能是Student类型
}
}
class MyVessel{ //从语法上讲把“E”取成别的名字如“QQ”也是可以的,但不规范。
private E obj;
public void setObject(E obj){
this.obj = obj;
}
public E getObject(){
return obj;
}
}
class Student{
String profession;
}
class Worker{
String company;
}
1、泛型方法的定义(与类的泛型捆绑)
方法要操作的类型不确定的,但是和调用该方法的对象指定的类型是一致。
2、泛型方法的定义(独立于类的泛型)
方法要操作的类型不确定的,而且不一定和调用该方法的对象指定的类型是一致。
3、泛型方法的定义(静态方法的泛型)
静态方法不能访问类上定义的泛型,因为它没有对象。如果静态方法需要泛型,该泛型只能定义在方法上。
代码示例:
class Demo{
//方法上的泛型和类的一致,或者说依赖于类的泛型
public void show(W w){
System.out.println("show:"+w.toString());
}
//不带泛型,不安全。因为在调用方可以把该方法的返回值强转成其它类型,从而出现强转异常
public Object myprint0(Object a){
System.out.println("myprint:"+a);
return a;
}
//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
public A myprint(A a){
System.out.println("myprint:"+a);
return a;
}
//静态方法带泛型,泛型一定要独立于类,因为它没有对象。
public static A myprint2(A a){
System.out.println("myprint:"+a);
return a;
}
}
这个不好解释直接代码示例了:
interface Inter{
public abstract V show(V v);
}
//实现泛型接口的类的定义。 方法中的参数类型和类声明实现接口时指定的类型一致,且已经确定为String!-----本例假设我们写这个类的时候知道该类就是专门处理String型数据的
class InterImpl implements Inter{
@Override
public String show(String s) {
System.out.println(s);
return s;
}
}
//实现泛型接口的类的定义。 方法中的参数类型和类声明实现接口时指定的类型一致,但不确定!-----本例假设我们写这个类的时候不知道该类是处理什么类型的数据的,但有一点确定:声明类对象时指定什么类型(泛型的实参),show方法就处理该类型
class InterImpl2 implements Inter{
@Override
public C show(C s) {
System.out.println(s);
return s;
}
}
(PS:关于Java泛型的高级应用,最低要求:虽然自己不一定能写出这样类似的定义,但是你必须会使用或调用(其他程序员或API中已经采用这种技术写的类或方法),所以这里只做最简单的介绍,有兴趣的博友可以去深入学习,博主能力有限,嘿嘿。。)
1、泛型的通配符:?
当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法, 这时泛型就用通配符?来表示即可。(助理解的比方: 泛型中的多态应用)
2、泛型的限定:
对操作的类型限制在一个范围之内。比如:定义一个功能,只操作Person类型或者Person的子类型。这时可以用:
? extends E:接收E类型或者E的子类型。这就是上限。
? super E: 接收E类型或者E的父类型。 这就是下限。
一般情况下:
只要是往容器中添加元素时,使用上限。 ? extends E
只要是从容器中取出元素时,是用下限。 ? super E
高级只有这么多,博友可以去API中看看这种情况,例如:Collection