Java---泛型

目录

泛型

泛型的使用

 泛型的擦除机制

 如何创建泛型数组

泛型的上界

通配符

通配符的上界

通配符的下界---父子类关系


泛型

如果我们想传入任意数据,数据的类型是多种多样的,可以使用Object类(任何类的父类)

class Array{
    Object []elem=new Object [10];
    int used=0;
    public void add(Object x){
        elem[used]=x;
        used++;
    }
    public Object get(){
        return elem[used-1];
    }

    @Override
    public String toString() {
        return "Array{" +
                "elem=" + Arrays.toString(elem) +
                ", used=" + used +
                '}';
    }
}
public class Text {
    public static void main(String[] args) {
        Array array=  new Array();
        array.add(1);
        array.add("hi");
        System.out.println(array);
    }
}

 类型需要强制转换:

 也就是说,从集合中取出一个元素,如果不知道元素是什么类型,贸然进行强制类型转换就会出错,因此有了泛型

泛型的使用

class Array<E>{//E相当于占位符
    E []elem=(E[])new Object [10];//这样定义泛型数组不会出错,但是是不正确的写法
    int used=0;
    public void add(E x){
        elem[used]=x;
        used++;
    }
    public E remove(){
        return elem[--used];
    }
    @Override
    public String toString() {
        return "Array{" +
                "elem=" + Arrays.toString(elem) +
                ", used=" + used +
                '}';
    }
}
public class Text {
    public static void main(String[] args) {
        Array<Integer> array = new Array<>();
//泛型---将类型参数化
        array.add(1);
        //array.add("hi");
        int ret=array.remove();
        System.out.println(ret);
    }

 泛型的意义:

  • 自动进行类型检查:传入数据类型必须和定义集合类时<>里传入的类型相同,编译期间会进行自动检查类型是否匹配
  •  自动进行强制类型转化:泛型的擦除机制,编译期间所有的E被擦除为了object,也就是说集合中得到的元素其实也是Object类型,需要进行强制类型转化,但是泛型自动进行了这一步

 泛型的擦除机制

Java编译期间E被替换为了Object

 如何创建泛型数组

1、E []elem=new E[10];这种创建方式为什么无法通过编译

根据擦除机制,编译期间转化为 Object []elem=new Object[10];

我们在Array类中新增一个方法:

 public E[] get(){
        return elem;
    }
 public static void main(String[] args) {
        Array<Integer> array = new Array<>();
        Integer[]ret=array.get();
        System.out.println(ret);
    }

 编译没有问题,运行时报错:object[]不能强制转化为Integer[]

 这里:我们来看一下数组之间的强制类型转换

 正确的转化方式是数组元素单个强转

  public static void main(String[] args) {
        Object[] array= {1,2};
        Integer []arr=new Integer [array.length];
        for (int i=0;i<arr.length;i++) {
            arr[i]=(Integer) array[i];
        }
        System.out.println(array);
    }

 为什么:Object类是所有类的父类,当然也就包括了包装类

Object数组可以容纳多种类型,但是Object数组向下转为一种包装类数组,有可能将”hi“强制转化为Integer,这是不安全的。

所以,泛型中:E []elem=new E[10];这种创建方式错误,极有可能造成Object数组向下转型的情况

2、E []elem=(E[])new Object [10];

 这样的写法和第一种是一样的,只不过是第一种的写法连编译都通不过,这个写法通过了编译,但是和第一个的擦除是一样的,是Object数组

3、通过反射创建---正确方法

 用反射编写泛型数组 - dedication - 博客园

泛型的上界

泛型只有上界没有下界

 class 泛型类名称<类型形参 extends 类型边界>

 类型边界可以是类和接口

 传入的E类型(实际是类)必须是类型边界的子类或者接口的实现类,没有定义边界,默认Object

 

 上界是Comperable接口

实现一个泛型类,找到数组的最大值

 为什么不对呢?E是引用类型包装类时,无法用>比较,使用Comparable来解决这个问题

要求:传入的类型参数必须实现了Comparable接口

 源码中只继承Comparable接口,所以不能使用Comparator

class  Max<E extends Comparable<E>>{
   public E find(E []arr){
       E max=arr[0];
       for (int i = 1; i <arr.length ; i++) {
           if(arr[i].compareTo(max)>0)
               max=arr[i];
       }
       return  max;
   }
}
public class Text {
    public static void main(String[] args) {
        Max<Integer> max=new Max();
        Integer[]arr={-127,128};
        System.out.println(max.find(arr));

        Max<String> max1=new Max();
        String []array={"hi","world"};
        System.out.println(max1.find(array));
    }

通配符

?在泛型的使用,就是通配符

private static<E extends Number> void print(Array<E> a){
        Array<E>  s=a;
        System.out.println(s);
    }
    
    private static void draw(  Array<?> a) {
        Object s = a;
        System.out.println(s);
    }

两个等价

通配符的上界

 Array这个泛型类实例化时,传入的类型只能是Number或者Number的子类

class Animal{

}
class Cat extends Animal{

}
  1. //传入的参数类型只能是Animal
    private static void print(ArrayList<Animal> cat){
    
    }
    
  2. //传入的参数类型是Animal和Animal的子类
    private static<E extends Animal> void print(ArrayList<E> cat){
    }
  3. //和2等价
    private static void print(ArrayList< ? extends Animal> cat){
    }

通配符的出现是为了解决父子类的问题

PriorityQueue<Object>//1
PriorityQueue<Number>//2 

类似于 PriorityQueue<E>,传入的Object和Number都是E,在编译期间被擦除为Object,没有父子类关系 

PriorityQueue<? extends Number>//1
PriorityQueue<Float>//2

对于1定义的PriorityQueue类,传入的类型参数必须要求是Number或着Number的子类,2中Float是Number的子类

通配符的下界---父子类关系

<? super 下界>

实例化对象时,传入的参数类型必须是下界的父类或下界本身

private static void print(ArrayList< ? super Integer > c){

}

ArrayList类创建对象时,传入的参数类型必须是Integer的父类或Integer

通配符的上界用于获取元素,下界用于添加元素

上界:最高E

传入类型:实例化泛型类时传入的类型必须是上界的子类或本身

 传入数据:传入数据的类型是上界的子类或本身

传入参数类型是上界的子类或本身,因为一个类的子类可能有很多个,例如Number类的子类就有Integer,Short,Float等,可以传入的数据类型太多了,编译器认为不安全

 接收数据时,因为子类太多,无法确定子类,使用上界接收

  下界:最低E,最高Object

传入类型:实例化泛型类时,传入类型必须是下界或者下界的父类

传入数据:下界类型最低是E,那么E的子类也可以传入

传入数据的类型是下界及下界的子类

 获取元素:因为传入数据的类型是下界及下界的子类,使用父类接收元素

 元素在编译期间转化为Object,使用父类接收要进行向下转化

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值