posts - 37, comments - 8, trackbacks - 0, articles - 0

java基础:关于泛型

Posted on 2008-08-05 10:12 梦与桥 阅读(1176) 评论(0)  编辑  收藏 所属分类: java基础

1、描述:泛型是类和接口的一种扩展机制,利用这种机制程序员可定义类或接口的集合。由泛型定义实际的类或接口时,只需为泛型的每个类型参数提供类型实参。
2、简单的泛型类声明,形如:pubic class ClassName<K,V>(){类体}
例子:

import java.util.Vector;
public class Test
{
    
public static void main(String args[])
    
{
        Stack
<String> strStack;
        strStack
=new Stack<String>(10);
        strStack.push(
" Generics Class.");
        strStack.push(
"am");
        strStack.push(
"");
        System.out.println(strStack.pop()
+strStack.pop()+strStack.pop());
    }

}

class Stack<T>
{
    
private int stackSize=100;//设置栈的大小
    Vector<T> stack=new Vector<T>();
    
public Stack(int size)
    
{
        stack.setSize(size);
    }

    
//入栈
    public boolean push(T o)
    
{
        
if(stack.size()>=stackSize)
        
{
            System.out.println(
"栈溢出");
            
return false;
        }

        
else
        
{
            
return stack.add(o);
        }

    }

    
//出栈
    public T pop()
    
{
        
if(!isEmpty())
        
{
            T o
=stack.lastElement();
            stack.remove(o);
            
return o;
        }

        
else
        
{
            System.out.println(
"栈空");
            
return null;
        }

    }

    
//判定栈是否为空
    public boolean isEmpty()
    
{
        
return stack.isEmpty();
    }

}

3、受限泛型类声明:所谓受限是指类型参数受限,其类型参数列表一般格式如:
<T  [extends]  类t1或接口t1[& 接口t2 [&  接口 t3]……]],
  E [extends]  类e1或接口e1[& 接口e2 [&  接口 e3]……]],
……
>

这样使其实参类型限制于某个类的派生类或接口实现类,或一组接口的实现类。
例子:
import java.util.Vector;
public class Test
{
    
public static void main(String args[])
    
{
        
//String不是有界参数的有效替代项
        
//Stack<String> strStack;
        Stack<Academician> acaStack=new Stack<Academician>(10);
        acaStack.push(
new Academician("张三",20,"西方文学"));
        acaStack.push(
new Academician("李四",21,"工商管理"));
        acaStack.push(
new Academician("王五",19,"计算机科学与技术"));
        System.out.println(acaStack.pop());
        System.out.println(acaStack.pop());
        System.out.println(acaStack.pop());
    }

}

//这里的Student称为类型参数的最左限制
//Stack<T>的最左限制为Object,因为任何类都是Object派生的
class Stack<extends Student>
{
    
private int stackSize=100;//设置栈的大小
    Vector<T> stack=new Vector<T>();
    
public Stack(int size)
    
{
        stack.setSize(size);
    }

    
//入栈
    public boolean push(T o)
    
{
        
if(stack.size()>=stackSize)
        
{
            System.out.println(
"栈溢出");
            
return false;
        }

        
else
        
{
            
return stack.add(o);
        }

    }

    
//出栈
    public T pop()
    
{
        
if(!isEmpty())
        
{
            T o
=stack.lastElement();
            stack.remove(o);
            
return o;
        }

        
else
        
{
            System.out.println(
"栈空");
            
return null;
        }

    }

    
//判定栈是否为空
    public boolean isEmpty()
    
{
        
return stack.isEmpty();
    }

}

class Student
{
    
private String name;
    
private int age;
    Student(String name,
int age)
    
{
        
this.name=name;
        
this.age=age;
    }

    
public String toString()
    
{
        
return "name:"+name+"\tage:"+age;
    }

}

class Academician extends Student
{
    
private String specialty;
    Academician(String name,
int age,String specialty)
    
{
        
super(name,age);
        
this.specialty=specialty;
    }

    
public String toString()
    
{
        
return super.toString()+"\tspecialty:"+specialty+"\n";
    }

}
       泛型类只是类的一种的扩展,其实体的类体与普通类基本一样,只是泛型类的实例变量的类型和实例方法的参数类型及返回值可以参数化而已。如例中:void push(T o)、T pop()。但由于泛型的类型参数在创建泛型类对象时才传入,是一个实例化参数,所以不能使用在泛型类的静态成员域中,也不能用在静态初始化块中。
        泛型类可以看做类的模板,程序员可以通过将具体的类名作为泛型类型参数的实参,传入泛型中定义新的类。如例中:Stack<Academician> acaStack=new Stack<Academician>(10);编译器将用所提供的类名Academician替换类型参数T,生成一个想像类Stack<Academician>,此类实际上并不存在,只是java编译器编译时当其存在。泛型的实参也可以使用泛型定义的类型,如:Stack<Stack<String>>  strStack=new Stack<Stack<String>>;
       作为泛型类型参数的实参,必须是引用数据类型,不能是基本数据类型。如果希望基本数据类型的值存储在堆栈中,可以使用其装类:Integer、Short等。
       用具体的类型名称作为泛型类型参数的实参,创建新类型的过程称为类型擦除。在这个过程中,所有的T将用最左限制类型替换掉,像Stack(T)擦除后,用其最左限制替换它。省略了泛型类型参数的类型被称为泛型的原生类型。
       假如使用泛型Stack创建了如下两个不同的类型:
Stack<Integer> I=new Stack<Integer>(10);
Stack<Integer> D=new Stack<Integer>(10);
显然,编译器会将其作为不同的类型(因此I和D之间不能进行类型转换),但在运行时共享相同的类名——可以使用instanceof进行测试。也就是说泛型生成的类具有相同的运行时类名称,也正因为如此,泛型生成类型的实例无法用instanceof关键字确定其类型。
4、通配符
         为了表示泛型定义的类型集合中的某个具体类型,需要为泛型的每个类型参数提供类型实参,否则编译器将会提示警告信息。为此要判断泛型Stack<T>产生的新类型:Stack<Integer>、Stack<Double>、Stack<String>是否为空,需要定义一组生载方法,很麻烦。为此,java提供了泛型参数通配符<?>,通配符可以代表任何类或接口(基本类型不包括在内)。看一个例子:
import java.util.Vector;
public class Test
{
    
public static boolean isEmpty(Stack<?> s)
    
{
        
return s.isEmpty();
    }

    
public static void main(String args[])
    
{
        Stack
<Integer> intStack=new Stack<Integer>(10);
        Stack
<Double> dblStack=new Stack<Double>(10);
        Stack
<String> strStack=new Stack<String>(10);
        
if(!isEmpty(intStack))
        
{
            System.out.println(
"intStack是空栈!");
        }

        
if(!isEmpty(dblStack)&&!isEmpty(strStack))
        
{
            System.out.println(
"dblStack和strStack都是空栈!");
        }

    }

}

class Stack<T>
{
    
private int stackSize=100;//设置栈的大小
    Vector<T> stack=new Vector<T>();
    
public Stack(int size)
    
{
        stack.setSize(size);
    }

    
//入栈
    public boolean push(T o)
    
{
        
if(stack.size()>=stackSize)
        
{
            System.out.println(
"栈溢出");
            
return false;
        }

        
else
        
{
            
return stack.add(o);
        }

    }

    
//出栈
    public T pop()
    
{
        
if(!isEmpty())
        
{
            T o
=stack.lastElement();
            stack.remove(o);
            
return o;
        }

        
else
        
{
            System.out.println(
"栈空");
            
return null;
        }

    }

    
//判定栈是否为空
    public boolean isEmpty()
    
{
        
return stack.isEmpty();
    }

}
java提供了两种方法,将通配符限制在某一个范围内:
(1)通配符的上界(<? extends 类或接口>):将通配符限制为指定的类及其派生类或接口的实现类。
(2)通配符的下界(<? super 类或接口>);将通配符限制为指定的类或其父类。
5、泛型类的继承问题:
方式一:
public class IntStack extends Stack<Integer>
{
    
public IntStack(int size)
    
{
        
super(size);
    }

    
public static void main(String args[])
    
{
        IntStack intStack
=new IntStack(10);
        intStack.push(
3);
        System.out.println(intStack.pop());
    }

}
方式二:SubStack(T)的T和Stack(T)的T相对应,保持一致。
public class SubStack<T> extends Stack<T>
{
    
public SubStack(int size)
    
{
        
super(size);
    }

    
public static void main(String args[])
    
{
        SubStack
<Integer> intStack=new SubStack(10);
        intStack.push(
3);
        System.out.println(intStack.pop());
        SubStack
<String> strStack=new SubStack(10);
        strStack.push(
"Hello");
        System.out.println(strStack.pop());
    }

}
6、泛型接口
(1)定义:
public interface Stackable<T>
{
    
boolean push(T o);
    T pop();
    
boolean isEmpty();
}
(2)实现:
public class ImpStack implements Stackable<String>
{
    
public boolean push(String s)
    
{省略具体实现}
    
public String pop()
    
{省略具体实现}
    
public boolean isEmpty()
    
{省略具体实现}

}

或者
public class ImpStack<T> implements Stackable<T>
{
    
public boolean push(T o)
    
{省略具体实现}
    
public T pop()
    
{省略具体实现}
    
public boolean isEmpty()
    
{省略具体实现}
}
7、泛型数组:java编译器不允许创建泛型生成类的数组,但允许用无界限通配符作为实参得到的类型定义数组,如:List<?>   ls=new   List<?> [2];这种类型的数组,简称为通配符数组。虽然通配符数组存在着类型安全问题,如果使用恰当还是非常灵活好用的。通配符数组的元素可以是任何泛型生成的具体类型的对象或其派生类的对象。例子:
import java.util.*;
class ArrayGeneric<T>
{
    
public static void listAll(List<?>[] ls)
    
{
        
for(List<?> obj:ls)
        
{
            listAll(obj);
        }

    }

    
public static void listAll(List<?> ls)
    
{
        
for(Object obj:ls)
        
{
            System.out.println(obj);
        }

    }

}

public class Test
{
    
public static void main(String args[])
    
{
        ArrayList al
=new ArrayList();
        LinkedList lls
=new LinkedList();
        al.add(
"123");
        al.add(
"156");
        lls.add(
"abc");
        lls.add(
"def");
        List
<?>[] ls=new List<?>[2];
        ls[
0]=al;
        ls[
1]=lls;
        ArrayGeneric.listAll(ls);
    }

}
注意:  
List<?>[] words =new List<?> [20];
Object[] objs=words;
objs[0]=3;
这三行能通过编译,但在运行时会发生错误
8、泛化方法:可对所有的方法进行泛化,包括实例方法、静态方法、构造方法。
例子:
import java.util.*;
public class Test
{
    
public <T> Test(T t)
    
{
        System.out.println(t);
    }

    
public static <T> void print1(T t)
    
{
        System.out.println(t);
    }

    
public <T> void print2(T t)
    
{
        System.out.println(t);
    }

    
public static void main(String args[])
    
{
        print1(
2008);
        print1(
"北京欢迎你!");
        Test test1
=new Test(2008);
        Test test2
=new Test("北京欢迎你!");
        test1.print2(
2008);
        test1.print2(
"北京欢迎你!");
    }

}

只有注册用户登录后才能发表评论。


网站导航: