【Java基础系列】第9章 泛型

9.1 深入泛型

泛型类型具体化的时候必须是基本类型中的包装类,例如:Integer、Double。int不行

9.1.1 泛型接口

(1)泛型接口概念:就是在定义接口的时候,在接口名后面加上<泛型类型,泛型类型...>

interface genericityInterface<K>{
    public K function();
}

(2)定义一个类实现该泛型接口:在实现泛型接口的时候,接口中的泛型也要传入具体的类型。如String,而在接口中用到泛型K的都需要在实现类中用具体类型String替代

public class infoClass implements genericityInterface<String>{ 
    @Override
    public String function() {

    }
}

(3)实例:

/**
 * 泛型接口
 * @param <K> 泛型类型
 */
interface genericityInterface<K>{

    /**
     * 泛型的方法
     * @return 返回泛型接口中对应的泛型类型
     */
    public K function();

}


/**
 * 实现泛型接口的类
 * 在实现泛型接口的时候,接口中的泛型也要传入具体的类型,如String
 */
public class infoClass implements genericityInterface< String >{

    private String[] study = new String[]{ "iphone", "Android","PHP" };

    /**
     * 重写接口的泛型返回值的抽象方法,类型也与对应泛型一致
     */
    @Override
    public String function() {
        Random rand = new Random();    
        //取0-2随机数,(nextInt(3)是包括0,不包括3)
        int randNum = rand.nextInt( 3 );
        return study[randNum];
    }

    /**
     * 主方法,调用该infoClass类
     * @param args
     */
    public static void main(String[] args) {
        infoClass genClass = new infoClass();
        System.out.println( genClass.function());
        System.out.println( genClass.function());
        System.out.println( genClass.function());
    }
}

9.1.2 泛型类

(1)泛型类概念:定义类的时候,在类名后面加上<泛型类型,泛型类型...>

public class genericityClass<K, T> {
    //定义一个泛型类型的属性key
    private K key;   
    //定义一个泛型类型的属性temp
    private T temp;  
}

(2)实例该类的时候,指定泛型的具体类型:在编译期,是无法知道K和T具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。

genericityClass<Double,Integer> gClass2 = new genericityClass<>(25.0,1314);

System.out.println(gClass2.getKey()+" "+gClass2.getTemp());

(3)例子:

/** 
 * 泛型类genericityClass<K, T> 。K、T分别为两个泛型类型
 */
public class genericityClass<K, T> {

    private K key;
    private T temp;
    //构造方法,传入两参数
    public genericityClass(K key, T temp) {
        this.key = key;
        this.temp = temp;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public T getTemp() {
        return temp;
    }

    public void setTemp(T temp) {
        this.temp = temp;
    }

    /**
     * 主方法
     * 在编译期,是无法知道K和T具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。可                    
     * 以看一下现在genericityClass类对于不同类型的支持情况
     */
    public static void main(String[] args) {
        genericityClass<String,String> gClass1 = new genericityClass<>("字符串1","字符串2");
        genericityClass<Double,Integer> gClass2 = new genericityClass<>(25.0,1314);
        genericityClass<Integer,Integer> gClass3 = new genericityClass<>(250,111111);
        System.out.println(gClass1.getKey()+" "+gClass1.getTemp());
        System.out.println(gClass2.getKey()+" "+gClass2.getTemp());
        System.out.println(gClass3.getKey()+" "+gClass3.getTemp());
    }
}

9.1.3 泛型方法

一个基本的原则是:无论何时,只要能做到,就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。

(1)泛型方法概念:定义方法时,在方法修饰符后加<泛型类型,泛型类型...>

public static<T> void methodOne(T t){ }

(2)调用该方法:传入指定的具体类型作为方法的参数,可以是任意类型。如果参数是泛型数组,传入的多个参数可以是不同类型

public static<T> void methodTwo(T... arr){
    for(T t : arr){
        System.out.println("methodTwo:"+t);
    }
}

//方法二可以传入多个不同类型的参数
methodTwo("iphone",520,1314.0,'A'); 

(3)例子:

/**
 * 泛型方法
 */
public class genericityMethod<K> {

    //方法一传入的类型是Object都可以
    public static<T> void methodOne(T t){
        System.out.println("methodOne:"+t);
    }

    //方法二参数是一个泛型数组,传入多个参数的类型可以不相同
    public static<T> void methodTwo(T... arr){
        for(T t : arr){
            System.out.println("methodTwo:"+t);
        }
    }

    //方法三泛型类型的方法不能是静态,因为泛型还没具体化,不能静态
    public <T> K methodThree( T t ){
        K k = null;
        System.out.println(t);
        //返回泛型对象
        return k; 
    }

    //主方法
    public static void main(String[] args) {
        methodOne("Abdroid");
        //方法二可以传入多个不同类型的参数
        methodTwo("iphone",520,1314.0,'A'); 
        //非静态的方法需要实例化本类
        genericityMethod<String> genMethod = new genericityMethod<>();
        //将返回的泛型对象输出
        System.out.println(genMethod.methodThree( 33 ));
    }
}

9.2 泛型通配符

9.2.1 类型通配符

        泛型通配符:<?>:表示泛型的具体类型不唯一,可以是Integer、Double等等...

9.2.2 通配符上界、通配符下界、无界通配符

1.[通配符上界]

? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”可以代表Animal或其子类,可代表不了Animal的父类

②对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))

  • [1] 如List<? extends Cat> 是 List<? extends Animal>的子类型)。
  • [2] 如 List<? extends Animal>是List<Animal>的子类型)
  • [3] List<?> 与List<? extends Objext>等同。

不能往List<? extends Animal> 添加任意对象,除了null。

比如fruits.add(new Strawberry()),这里是add一个Strawberry类型对象,而fruits要求add的对象类型必须是Fruit或它的任何某种子类型对象,这个某种类型能用Strawberry或Fruit或Apple来表示吗?

④在List<? extends Animal> 可以是Animal类对象或Bird对象等(只是某一类对象),反过来说,在List<? extends Animal> list里的都是Animal对象,即Bird也是Animal对象,Cat也是Animal对象(用java的语言来说就是子类可以指向父类,父类却不能指向子类),那么在Animal里的所有方法都是可以调用的

2.[通配符下界]

Super Cat”则表示通配符“?”的界为Cat,换句话说就是,“? Super Cat”可以代表Cat或其父类,可代表不了Cat的子类

②对于通配符的下界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))

  • [1]如List<? super Bird>是List<? super Animal>的子类型。
  • [2]如List<Animal> 是 List<? super Animal>的子类型。

3.[无界通配符]

知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如List<?>,“?”可以代表任意类型,“任意”也就是未知类型。

[1]List<Object>与List<?>并不等同,List<Object>是List<?>的子类。还有不能往List<?> list里添加任意对象,除了null。

例子:

/**
 * 泛型通配符 <?> 
 * @param <T>
 */    泛型T继承了Number类才可以使用其中的doubleValue()等方法
public class AverageClass<T extends Number> {

    T temp;

    public T gettemp() {
        return temp;
    }

    public void settemp(T temp) {
        this.temp = temp;
    }

    //将输入的double转为int,方便比较
    public int cast(){
        return (int) temp.doubleValue();
    }

    //自定义的对比方法
    public boolean equalsAver( AverageClass<?> aver ){
        if( aver.cast()==this.cast()){
            return true;
        }else{
            return false;
        }
    }

    public static void main(String[] args) {
        AverageClass<Integer> ave1 = new AverageClass<>();
        //传入Integer类型
        AverageClass<Integer> ave2 = new AverageClass<>();
        //传入Double类型
        AverageClass<Double> ave3 = new AverageClass<>(); 
        ave1.settemp(123);
        ave2.settemp(124);
        ave3.settemp(123.0);
        System.out.println(ave1.equalsAver(ave2)); //false
        System.out.println(ave1.equalsAver(ave3)); //true
    }

}

9.3 擦除和转换

9.3.1 擦除的概念

定义了一个List<Integer>对象li,赋给一个不带泛型的List变量list后,编译器 就会流失前者的泛型信息,这就是擦除

9.3.2 擦除的例子

class Demo<T extends Number>  {  

    //T类型的属性size
    T size;    

    public Demo(){ }  

    public Demo(T size){  
        this.size = size;  
    }  

    public void setT(T size){  
        this.size = size;  
    }  

    public T getSize(){  
        return this.size;  
    }  

}  


class TestEraser{  

    public static void main(String[] args){  
        //method1();  
        method2();
        //method3();  
    }  

  
    /**
    * 把一个d赋给不带泛型信息的d2变量时,编译器会流失d对象的泛型信息,但因为Demo类型的参数类型
    * 上限是Number类,所以编译器依然知道d2.getSize()的返回值类型是Number类型。
    */  
    public static void method1(){  
        Demo<Integer> d = new Demo<>(6);  
        Integer id = d.getSize();  
        //把d对象赋给Demo类型的变量,会丢失泛型信息
        Demo d2 = d; 
        //编译错误,因为d2没了泛型。所以不能用Integer承接,只能用上限Number 
        //Integer id2 = d2.getSize();
        Number id2 = d2.getSize();  

    }  

  

   /**
    * 下面程序中定义了一个List<Integer>对象,当把对象li赋给一个不带泛型的List变量list后,编
    * 译器 就会流失前者的泛型信息,这就是擦除。但java允许直接把List对象赋给一个List<Type>
    *(Type可以是 任何类型)的变量。 从逻辑上看,List<String> 是list的子类,如果直接把一个
    * List对象赋给一个List<String>对象 将引发编译错误,但对于泛型而言,可以直接把一个List对象
    * 赋给一个List<String>对象,编译器 仅仅提示“未经检查的转换”,但要访问List<String>对象中
    * 的元素将会引发ClassCastException异常
    */  
    public static void method2(){  
        List<Integer> li = new ArrayList<>();  
        li.add(6);  
        li.add(9);  
        List list = li;  
        //警告,编译运行完全正常  
        List<String> ls = list; 
        //但要访问ls集合里的元素,将会引起运行时异常  
        System.out.println(ls.get(0));   
    }  

   /**
    * method2代码虽然可以编译通过,但实际上对list变量实际上引用的是List<Integer>集合,所以当
    * 试图把集合里的元素当成String类型的对象取出时,将引发ClassCastException类型转换异常,上
    * 面的代码 可以转换成下面的代码就好理解了。
    */    
    public static void method3(){  
        List li = new ArrayList();  
        li.add(6);  
        li.add(9);  
        //运行异常  
        System.out.println((String)li.get(0));   
    }  

}

9.4 泛型与数组

        ①不能创建持有泛型的数组对象。

        ②不能创建泛型类型的数组: 擦除类型,转换为Object[]

        ③例子:

List<String>[ ]  ts;
//编译出错,不能实例化泛型数组 
ts = new List<>[10 ];   

List[ ] list = new List[ ]( );
//不可以定义泛型数组,但可以转换
Ts = (List<String>[ ]) list; 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Yvan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值