Java中的泛型详解(一)

导读:本篇文章讲解 Java中的泛型详解(一),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

泛型

什么是泛型

泛型的引入

  1. 我们思考第一个问题,如何定义一个数组,使得数组中什么类型的元素都可以存放?
    第一时间我们会想到顶一个Object类型的数组,因为所有类型的父类都继承于Object,那么通过向上转型我们可以定义出这样一个数组:Object[] array = new Object[10](假设数组的容量为10)
  2. 我们再思考第二个问题,如何在一个类中定义一个数组,这个数组可以存放任何类型的数据,而且定义一个方法该方法可以获得指定数组下标的元素?
在这里插入代码片class Marry {
    public Object[] objects = new Object[10];

    //数组的pos下标位置存放元素val
    public void setVal(int pos,Object val) {
        objects[pos] = val;
    }

    //获取数组pos位置下标的元素并返回
    public Object getVal(int pos) {
        return objects[pos];
    }
}
public class Test {
    public static void main(String[] args) {
        Marry marry = new Marry();
        marry.setVal(0,1);
        marry.setVal(1,2.5);
        marry.setVal(2,"hello");
        marry.setVal(3,"ok");
        
        int ret1 = (int) marry.getVal(0);
        double ret2 = (double) marry.getVal(1);
        String ret3 = (String) marry.getVal(2);

    }
}


我们可以看出这里实现了一个类中定义一个Object数组可以存放任何类型的元素(十分方便),但是在获取的时候对应下标元素的时候,每一次调用方法的时候都必须进行强制类型转换(十分麻烦),那这个时候我们想有没有一种方法可以让我们在存放元素的时候可以存放任何类型的数据,获取元素的时候不需要每次都进行强制类型转换呢?答案就是泛型

泛型的概念

泛型就是把传入的类型进行参数话,通俗易懂的说就是把类似于int double String Student Animal 这些引用类型当成参数进行传入,这样让编译器在编译的时候自动进行类型的检查和转换。

泛型的语法

这里是引用class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, …, Tn> {
}

注意:这里类型形参中的字母可以用任何字母表示,但是一般用T,且参数列表可以有多个。

示例

现在引入了泛型的该概念,我们将上述的代码进行改进,代码如下:

在这里插入代码片class Marry<T> {
    public T[] objects = (T[]) new Object[10];

    //数组的pos下标位置存放元素val
    public void setVal(int pos,T val) {
        objects[pos] = val;
    }

    //获取数组pos位置下标的元素并返回
    public T getVal(int pos) {
        return objects[pos];
    }
}
public class Test {
    public static void main(String[] args) {
        Marry<Integer> marry = new Marry<Integer>();
        marry.setVal(0,1);
        marry.setVal(1,10);
//        marry.setVal(2,2.5);//此时只能传入Integer类型的数据,故编译报错
//        marry.setVal(3,"hello");//此时只能传入Integer类型的数据,故编译报错
//        marry.setVal(4,"ok");//此时只能传入Integer类型的数据,故编译报错
        
        int ret1 = marry.getVal(0);
        int ret2 = marry.getVal(1);

    }
}

这里我们可以看出,当指定Marry这个类为泛型类时,在new对象时必须指定传入的类型是什么(也可以不指定,不指定时为裸类型,一般不支持这么写,裸类型只是为了兼容旧版本的API),如上述代码,指定了传入的类型为Integer时,我们在存入数据的时候只能存入int类型的,存入其他类型的时候就会报错,而且我们在获取数据的时候不需要进行强制类型转换,这是为什么呢?这就涉及到了泛型的擦除机制!!!

泛型的擦除机制

通过上述的讲解,我们知道泛型的主要作用就在于编译的时候(注意泛型只作用于编译的时候,程序运行的时候没有泛型的概念),自动对类型进行检查和转换,其原理则是泛型的擦除机制。
泛型的擦除机制:即在编译的时候,把所有的T类型擦除成Object(可通过编译后的字节码文件进行查看,这也就是说为什么泛型只作用于编译的时候,程序运行的时候没有泛型的概念)。

泛型的上界

泛型的语法

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

}

注意:这里的类型边界既可以是接口也可以是类,没有指定边界时默认继承Object类

示例

假设我需要求一个数组中的最大值,代码如下:

class Alg<T extends Comparable<T>> {
    public T findMax(T[] arr) {
        T max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].compareTo(max) > 0) {
                max = arr[i];
            }
        }
        return max;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Integer[] arr = {1,2,3,4};
        Alg<Integer> alg = new Alg<>();
        Integer ret = alg.findMax(arr);
        System.out.println(ret);
    }
}

在这里插入图片描述
注意:

  1. 这里的泛型类中必须继承Comparable接口,这表示传入进来的参数类型必须实现了Comparable接口(因为这里的T被擦除成Object类,但是Object类并没有实现Comparable接口)
  2. 因为传入的类型都是引用类型,引用类型必须实现Comparable或者Comparator接口,所以如果传入的自定义的类型如:Person,Student同样必须实现Comparable或者Comparator接口

泛型方法

语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { …}

静态泛型方法

public class Util {
//静态的泛型方法 需要在static后用<>声明泛型类型参数
	public static <E> void swap(E[] array, int i, int j) {
		E t = array[i];
		array[i] = array[j];
		array[j] = t;
	}
}

静态的泛型方法 需要在static后用<>声明泛型类型参数,如果该泛型有上界,与遵循泛型上界的语法,在类型后面加上extends即可。

普通泛型方法

public class Util {
	public <E> void swap(E[] array, int i, int j) {
		E t = array[i];
		array[i] = array[j];
		array[j] = t;
	}
}

同静态泛型方法一样,只需在返回值前面声明泛型类型参数,也循环泛型上界的语法。

Java中的泛型详解(二)中将介绍一下泛型中的通配符的相关知识

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/89458.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!