【03】泛型限定通配符

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

泛型限定通配符

1.泛型的继承和子类型

1.1以下泛型类的继承关系不成立

1)给定两种具体的类型A和B(例如Fruit和Apple)无论A和B是否相关,MyClass<A>与MyClass<B>都没半毛钱关系,它们的公共父类是Object.2)A父类是B
Java中List<A>
List<A>
Plate<A>不变 它的父类不会是Plate<B>?
因为没有继承关系就不能够强转:
Plate<A> a = plate;3)这个案例属于泛型类型的类型参数T不一样,所以继承关系就不成立。虽然A继承自B,但是当A和B类分别作为泛型类的类型参数后,各自泛型类的继承关系就不成立了。

1.2以下泛型类的继承关系成立

在这里插入图片描述

(1)只要泛型类的类型参数T保持数据类型一致,无论子类型如何扩展泛型的类型参数,泛型类的继承关系都是成立的。

(2)泛型类的继承关系代码

package com.generics.demo3.plate;

public interface Plate<T> {
    
    

    public void set(T t);

    public T get();

}
package com.generics.demo3.plate;

import java.util.ArrayList;
import java.util.List;

/**
 * @PackageName:com.generics.demo3.plate
 * @ClassName:AIPlate
 * @Description: 泛型类型定义
 * @author:青风百草
 * @date:2020/9/2 10:22
 */
public class AIPlate<T> implements Plate<T> {
    
    

    private List<T> items = new ArrayList<>();


    @Override
    public void set(T t) {
    
    
        items.add(t);
    }

    @Override
    public T get() {
    
    
        int index = items.size() -1;
        if (index >= 0) {
    
    
            return items.get(index);
        } else {
    
    
            return null;
        }
    }
}

package com.generics.demo3.plate;

/**
 * @PackageName:com.generics.demo3.plate
 * @ClassName:BigPlate
 * @Description:
 * @author:青风百草
 * @date:2020/9/2 10:39
 */
public class BigPlate<T> extends AIPlate<T>{
    
    

}

package com.generics.demo3.plate.inheritedclass;

import com.generics.demo3.plate.BigPlate;

/**
 * @PackageName:com.generics.demo3.plate.inheritedclass
 * @ClassName:ColorPlate
 * @Description:
 * @author:青风百草
 * @date:2020/9/2 10:45
 */
public class ColorPlate1<K,T> extends BigPlate<T> {
    
    

}

package com.generics.demo3.test;

import com.generics.demo3.fruit.Apple;
import com.generics.demo3.plate.AIPlate;
import com.generics.demo3.plate.BigPlate;
import com.generics.demo3.plate.Plate;
import com.generics.demo3.plate.inheritedclass.ColorPlate1;

/**
 * @PackageName:com.generics.demo02.test
 * @ClassName:Test4
 * @Description:
 * @author:青风百草
 * @date:2020/9/2 10:18
 */
public class Test {
    
    

    /*
     *泛型类型继承:ColorPlate1---> BigPlate --->AIPlate --->Plate
     *@Author 青风百草
     *@Description 无论ClorPlate怎么扩展,只要Apple不变,这种泛型类的继承关系还是存在的。
     *@Date 10:58 2020/9/2
     *@Param []
     *@return void
     **/
    public static void test1(){
    
    
        Plate<Apple> applePlate1 = new AIPlate<Apple>();
        Plate<Apple> applePlate2 = new BigPlate<Apple>();
        Plate<Apple> applePlate3 = new ColorPlate1<String,Apple>();
    }

    public static void main(String[] args) {
    
    
        test1();
    }
}

2.通配符

2.1上界通配符

在这里插入图片描述

(1)上界通配符泛型类的定义方式

Plate <? extends 类型>

(2)上界通配符是为了限定泛型类的类型参数中的数据类型可以是指定类型参数的数据类型的派生类型,即它的子类。这样的好处是方便泛型类型对象赋值。

(3)例如:

Plate<? extends Fruit>这个泛型类的实际类型参数,只能是Fruit类型的子类。

2.2转型方便带来的后遗症

在这里插入图片描述

2.2.1存放数据时的问题

(1)就是泛型对象无法再存放任何的元素。
(2)放null可以。
(3)虽然通过反射能够放数据,但是什么都能放了,安全性就没办法保证。
在这里插入图片描述

2.2.2取出数据时的问题

(1)就不清楚得到的数据具体是什么数据类型。

2.3下界通配符

在这里插入图片描述

(1)下界通配符泛型类的定义方式

Plate <? super 类型>

(2)表示的意思是泛型类的类型参数的数据类型,只能是该类型的父类和基类

(3)例如:

Plate<? super Fruit>这个泛型类的实际类型参数,只能是Fruit类型的父类和基类。

(4)使用下界通配符的后遗症是什么?
在这里插入图片描述

意思就是数据拿出来的时候就不清楚具体是什么数据类型了,只能以限定类的父类或基类Object存放。

2.4非限定通配符

1)Plate<?>:这就称作非限定通配符.2)它是一个泛型类型,但是泛型的类型参数数据类型是未知的。
(3)等价于Plate<? extends Object>

2.4.1非限定通配符的副作用

在这里插入图片描述

(1)是即不能写也不能读

2.4.2不能读不能写,那泛型有什么用呢

(1)它可以保证泛型可以检查类型
(2)例如:

List :这种情况不会对数据类型进行安全检查
List<?>:编译器会进行数据类型的安全检查

2.5限定通配符

1)Plate<? extends T>:上界限定通配符
(2)Plate<? super T>:下界限定通配符
这两种情况统称为限定通配符。

2.5.1上界限定通配符的副作用

在这里插入图片描述

(1)只能读,不能写.

2.5.2下界限定通配符的副作用

在这里插入图片描述

(1)只能写,不能读

3.通配符与泛型有什么关系?

1)因为泛型<T>:如果这样声明,就只能写具体的某一种泛型类型,会遇到数据转型的一个问题,参考1.1的描述,而使用通配符让类型转换变得更灵活。

4.Java泛型的PECS原则

1)如果你只需要从集合中获得类型T,使用<? extends T>通配符
(2)如果你只需要将类型T放到集合中,使用<? super T>通配符
(3)如果你既要获取又要放置元素,则不使用任何通配符。
例如:	List<Apple>4)PECS即Producer extends Consumer super,为了便于记忆。
(a)Producer代表的是生产者,不能写,只能读。
(b)Consumer代表的是消费者,可以写,不能读。

4.1泛型为什么需要PECS原则?

1)提升API的灵活性。即类型转换的灵活性。
(2<?>既不能存又不能取

5.使用通配符的目的是什么?

(1)就一个目的,为了灵活的转型

6.泛型的应用举例

6.1涉及的实体类

(1)Fruit

package com.generics.demo01.fruit;

/**
 *水果
 */
public class Fruit {
    
    
	@Override
	public String toString() {
    
    
		return "水果";
	}
}

(2)Apple

package com.generics.demo01.fruit;

/**
 * 苹果
 */
public class Apple extends Fruit{
    
    

	private int id;

	public Apple(){
    
    

	}

	public Apple(int id){
    
    
		this.id = id;
	}

	@Override
	public String toString() {
    
    
		return "Apple{" +
				"id=" + id +
				'}';
	}
}

(3)Banana

package com.generics.demo01.fruit;

/**
 *香蕉
 */
public class Banana extends Fruit {
    
    

	private int id;

	public Banana() {
    
    
	}

	public Banana(int id){
    
    
		this.id = id;
	}

	@Override
	public String toString() {
    
    
		return "Banana{" +
				"id=" + id +
				'}';
	}
}

6.2应用

(1)每一个步骤都是因为前一步骤遇到问题才出现的下一个方法,可以结合代码思考错误原因,为什么用到下一方法来解决问题。

package com.generics.demo3.test;

import com.generics.demo01.fruit.Apple;
import com.generics.demo01.fruit.Banana;
import com.generics.demo01.fruit.Fruit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @PackageName:com.generics.demo3.test
 * @ClassName:Test2
 * @Description:
 * @author:青风百草
 * @date:2020/9/2 17:07
 */
public class Test2 {
    
    

    public static void biz1(){
    
    

        //================s1.使用集合实现复制=======================
        List<Apple> src1 = new ArrayList<>();
        src1.add(new Apple(1));
        List<Apple> dest1 = new ArrayList<>(10);
        dest1.add(new Apple(2));
        System.out.println(dest1);
        copy1(dest1,src1);
        System.out.println(dest1);

        //=================s2.使用泛型实现复制======================
        List<Banana> src2 = new ArrayList<>();
        src2.add(new Banana(1));
        List<Banana> dest2 = new ArrayList<>(10);
        dest2.add(new Banana(2));
        //s1.此时已经无法复制,解决办法是使用限定通配符
        //copy1(dest2,src2);
        //s2.使用泛型之后无论是苹果还是香蕉都可以存取了
        copy2(dest2,src2);

        //=================s3.使用通配符实现复制======================

        //s3.但是当我遇到如下情况,当是一个水果盘子的时候,再调用copy1,发现又不能放了,为什么
        List<Fruit> dest3 = new ArrayList<>();
        dest3.add(new Banana());
        //copy2(dest3,src2);
        //使用通配符
        copy3(dest3,src2);

        //==================s4.使用上界与下界通配符结合的方式==============

        List<Fruit> dest4 = new ArrayList<>();
        dest4.add(new Banana());
        //如果泛型限定了泛型类型为Fruit,则List<Banana>无法放入泛型,此处就需要用到上
        // 界限通配限定符来满足这一情况
        //Test2.<Fruit>copy3(dest4,src2);
        Test2.<Fruit>copy4(dest4,src2);

    }

    public static void copy1(List<Apple> desc,List<Apple> src){
    
    
        Collections.copy(desc,src);
    }

    //s2.使用泛型
    public static <T> void copy2(List<T> desc, List<T> src){
    
    
        Collections.copy(desc,src);
    }

    //s3.使用下界限定通配符
    public static <T> void copy3(List<? super T> desc, List<T> src){
    
    
        Collections.copy(desc,src);
    }

    //s4.使用上界限定通配符
    //(1)List<? extends T> src是生产者,可以从里面取数据出来
    //(2)List<? super T> desc是消费者,可以向里面写入数据
    public static <T> void copy4(List<? super T> desc, List<? extends T> src){
    
    
        Collections.copy(desc,src);
    }

    public static void main(String[] args) {
    
    
        biz1();
    }
}

7.泛型的应用场景

7.1泛型在集合中的使用

在这里插入图片描述

7.2泛型在RxJava中的应用

在这里插入图片描述

8.练习

(1)理解如下写法的含义及区别
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiogjie_67/article/details/108628503
今日推荐