泛型:在定义类的时候并不会设置类中的属性或方法中参数的具体类型,而是在使用类时候在进行定义。若是在使用时不规定具体类型,则默认按Object型处理,会警告但不会报错。
泛型的意义:
·会在编译期间对类型进行自动检查
·会自动进行类型转换
使用泛型需要注意的问题:
·不能new泛型类型的数组new T[]
·泛型的编译过程是对类型的擦除,将T擦除成Object类,而不是替换
·擦除机制:向上擦除,即向基类的方向擦除
·不能new泛型类型的对象,即: T t = new T();错误
·不能new泛型对象的数组,即 Object[] o = new Generic[10];错误
·简单类型不能作为泛型的参数,因为简单类型没有基类,无法进行擦除操作
·泛型的上界:T extends 上界,泛型没有下界
·在static方法中不能用泛型类型的参数,因为static定义的方法不依赖于对象,无法指定泛型类型
泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法的调用。泛型方法可以定义在泛型类或接口中,也可以单独定义。
定义泛型方法的规则:
·所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子
中的)。
·每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于
指定一个泛型类型名称的标识符。
·类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
·泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
看以下例子:
/** 用一个数组实现不同的数据类型 */
public class Test{
// 在这里只是一个泛型标记的声明
public static void printArray(T[] Array){
for (T element : Array){
System.out.print(element + " ");
}
System.out.println();
}
public static void main( String args[] ){
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray );
System.out.println( "双精度型数组元素为:" );
printArray( doubleArray );
System.out.println( "字符型数组元素为:" );
printArray( charArray );
}
}
程序的输出结果是:
整型数组元素为:
1 2 3 4 5
双精度型数组元素为:
1.1 2.2 3.3 4.4
字符型数组元素为:
H E L L O
带界的类型参数:
有时候,我们需要限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界,即 T extends 上界,泛型没有下界。
看例子:
/** 求三个元素中最大的一个 */
public class Test{
/* Object类中没有comareTo()方法,此方法存在于Comparable接口中,因此
* 向上擦除到此类就好,故设置上界为Comparable接口
*/
public static > T max(T x, T y, T z){
T max = x;
// 对象的比较用compareTo()方法,而不是大于小于号
if ( y.compareTo( max ) > 0 ){
max = y;
}
if ( z.compareTo( max ) > 0 ){
max = z;
}
return max;
}
public static void main( String args[] ){
System.out.println(max(3, 4, 5));
System.out.println(max( 6.6, 8.8, 7.7 ) );
System.out.println(max( "你", "我", "他" ) );
}
}
结果是:
5
8.8
我
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
看例子:
/** 用泛型实现一个简单的栈 */
class GenericStack{
private T[] elem;
private int top;
public GenericStack(){
this(10);
}
public GenericStack(int size){
// 不能new泛型类型的数组new T[]
this.elem = (T[])new Object[size];
this.top = 0;
}
/** 元素压栈 */
public void push(T val){
this.elem[this.top++] = val;
}
/** 元素出栈 */
public void pop(){
// 元素出栈后该位置置为null,防止内存泄漏
this.elem[this.top-1] = null;
--this.top;
}
/** 获取栈顶元素 */
public T getTop(){
return this.elem[this.top - 1];
}
}
public class Test{
public static void main(String[] args) {
GenericStack s1 = new GenericStack();
s1.push(10);
s1.push(20);
int data = s1.getTop();
System.out.println(data);
}
}
结果是:20
通配符多用于类库的开发,在Java源码里可以看到很多通配符的使用。通配符是用“?”代替具体的类型参数。例如 集合类接口List< ?> 在逻辑上是List、List 等所有List<具体类型实参>的父类。
·通配符也会进行类型的擦除,即向上擦除到Object类。
·通配符的上界: extends T>,表示向上擦除的边界
·通配符的下界: super T>,表示寻找是否有T类型的基类实现了Comparable接口。
即:
< ? extends T> 表示该通配符所代表的类型是T类型的子类。
< ? super T> 表示该通配符所代表的类型是T类型的父类。
阿里巴巴Java开发规约规定:
泛型通配符< ? extends T >来接收返回的数据,此写法的泛型集合不能使用 add 方法,而 < ? super T> 不能使用
get 方法,做为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则:
第一、频繁往外读取内容的,适合用 extends T >。
第二、经常往里插入的,适合用 super T> 。
看例子:
import java.util.*;
class GenericAlg {
//下界:是否有T类型的基类实现了Comparable接口
public staticsuper T>> T findMaxPerson(ArrayList list){
T maxPerson = list.get(0);
for(int i = 1;i < list.size();i++){
if(maxPerson.compareTo(list.get(i)) < 0){
maxPerson = list.get(i);
}
}
return maxPerson;
}
}
/** 继承了Comparable接口并重写了compareTo()方法,故Person类就是通配符的下界 */
class Person implements Comparable{
private String name;
public Person(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
@Override
public int compareTo(Person p) {
return name.compareTo(p.name);
}
}
public class Test{
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Person("你"));
list.add(new Person("我"));
list.add(new Person("他"));
System.out.println(GenericAlg.findMaxPerson(list));
}
}
结果:Person [name=我]