JAVA泛型详解(上)

*1.泛型定义*
泛型是在定义接口,类时指定类型形参,这个类型形参将在创建对象,声明变量时明确指定。JDK5集合框架中的大量使用了泛型,所以在使用集合框架时可以明确指定具体的类型参数。下图声明了一个类使用泛型:

public abstract class PhoneFactory<T> {

    private T factory;


    public abstract Phone productPhone(T t);

    public T getFactory() {
        return factory;
    }

    public void setFactory(T factory) {
        this.factory = factory;
    }

}

2.从泛型派生出子类
如果父类或接口定义时采用了泛型,则子类继承或实现接口时,父类或接口中声明的泛型参数必须指明具体的类型或不指定。
(1):如果父类或者接口中方法参数使用参数,则子类在实现父类或接口时,必须明确指定具体的类型,否则编译报错:
a.父类代码如下:

public abstract class PhoneFactory<T> {

    private T factory;


    public abstract Phone productPhone(T t);

    public T getFactory() {
        return factory;
    }

    public void setFactory(T factory) {
        this.factory = factory;
    }

}
b.子类代码如下:
/**
 * 子类实现扩展带有泛型的父类时,此时父类不能包含形参类似 **PhoneFactory<T>,
 * 必须指明一个具体的类型
 *
 */
public class AppleFactory extends PhoneFactory<Phone> {

    @Override
    public Phone productPhone(Phone phone) {    
        return new ApplePhone();
    }
}

(2).如果父类或接口中的方法中没有使用泛型,则定义子类时可以不指定具体的类型,JDK编译警告:

public interface CarProductService<T> {

    public void productCar(String carType);
}


public class BenzCarProductService implements CarProductService {

    @Override
    public void productCar(String carType) {
        // TODO Auto-generated method stub

    }

}

3.泛型注意事项
(1).静态变量不能使用泛型,静态变量属于类级别不属于某个实例的。
(2).并不存在具体的泛型类,这个可能不是很好理解,通过例子很容易看明白,例如:ArrayList 与ArrayList属于同一个类,JVM编译之后不会生成两个class文件。
这里写图片描述
a.通过程序实验结果可以看出不存在具体的泛型类。
4.类型通配符上限
(1).对于已经定义了泛型的类或接口,在使用带有泛型的类或接口(创建对象或声明变量)时,如果没有传入具体的类型参数,会引发编译警告。大多数情况下,对于定义的方法或类中的参数类型不确定,这个时候使用类型通配符,类型通配符就是一个问号 (? )
a.请看如下代码,定义了一个方法遍历集合元素,List中已经使用泛型,程序中没有使用,引起编译警告:
这里写图片描述
b.上述程序不是很好的写法,请看下面使用List:

public void test(List<Object> list){
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
b1.将程序修改为List<Object>是不会引起编译警告,但是在实际调用时可能不是我们所期望的。比如,我们在调用时传入List<String>,则会发生编译错误:

这里写图片描述
b2.上述程序反应一个问题,List并不是List的子类。在JAVA中,如果T中使用了泛型,A是B的一个子类,则T《A》并不是T《B》的子类型。
(2).将上述方法进一步改进,可以在方法参数上修改为List<?>,这样在调用test方法时,无论传入List<String>还是List<Integer>都不会发生错误
这里写图片描述
(3).在定义接口或类时,可以使用泛型,但是不能使用通配符,只有在使用定义了泛型的类或接口时,才可以使用通配符?
5.类型通配符上限
1.再说类型通配符之前,先看学习一下泛型的继承。在JAVA中如果父类T有两个子类T1,T2,则类A<T>不是类A<T1>和类A<T2>的父类。没有继承关系。
这里写图片描述
2.上述图中,可以用一个具体例子解释一下:
2.1定义三个类,Phone,HTCPhone,ApplePhone。Phone是父类。其他两个为子类。
这里写图片描述
2.2 定义生产Phone的服务,采用泛型方式声明。如下
这里写图片描述
2.2.1 具体实现代码如下:

public interface PhoneSaleService<T> {


    public void salePhoneService(PhoneSaleService<T> sale);


}

2.2.2 实现类如下:

public class HtcPhoneSaleService<T> implements PhoneSaleService<T> {

    @Override
    public void salePhoneService(PhoneSaleService<T> sale) {
            System.out.println("*******采购手机,下订单******");
            System.out.println("****进货**");
            System.out.println("****登记入库**");
    }
}

2.2.3 : 测试类:
这里写图片描述
通过上述案例可以看出,PhoneSaleService<Phone>并不是PhoneSaleService<HTCPhone>的父类,引起编译错误。这个在泛型中应该特别注意。
3.为解决上述问题,可以采用通配符的方式来解决。JAVA中对于泛型还采用了类型限制,也是通过通配符来实现。

public interface PhoneSaleService<T extends Phone > {

    public void salePhoneService(PhoneSaleService<? extends Phone> sale);

}

实现类:

public class HtcPhoneSaleService<T extends Phone> implements PhoneSaleService<Phone> {

    @Override
    public void salePhoneService(PhoneSaleService<? extends Phone> sale) {
            System.out.println("*******采购手机,下订单******");
            System.out.println("****进货**");
            System.out.println("****登记入库**");
    }
}

测试类,就不会报上述编译错误:
这里写图片描述
5.类型通配符下限

6.泛型方法

6.1 泛型方法形式如下:修饰符 <T,S> 方法返回类型 方法名(泛型参数列表)。
6.2 泛型方法与普通方法区别就是在修饰符后面,方法返回类型前面用<>使用了修饰符。其中,方法参数列表中也使用了泛型参数。
6.3泛型方法主要阐明了方法中一个参数或者多个参数之间的依赖的关系,或者方法返回值与方法参数之间的依赖关系。没有依赖关系,则没有必要使用泛型方法。
6.4 泛型方法必须要有修饰符。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章