简析Java泛型使用与原理

Java泛型被许多人称作一颗语法糖(添加某种语法,以增加程序的易读易写性),是因为它C#相比只能算是“伪泛型”。为什么这么说呢?泛型到底是什么呢?

写程序的时候经常会用到数组,可以用数组来存放一个数据集合。如果让你数组存三个数——1,2,3,相信你很快就能写出代码:

		int[] arry = new int[3];
        arry[0] = 1;
        arry[1] = 2;
        arry[2] = 3;

那么要是不存放数字,而是字符串11,22,33呢?这也很简单,声明数组的时候使用String类型,再把对应的字符串存进去就好了,当然想要获取数组里边的值也很简单。
那么问题来了,如果我想要既想存整数,又想存字符串该怎么办呢?这时候只利用数组就做不到了。如果你硬往刚才的arry数组里放一个字符串的话,IDE肯定是提示你不能这样做的。这时候可以使用一个新的工具List。它不光可以存基本数据类型,还可以存储对象:

		Book book = new Book();
		List list = new ArrayList();
        list.add(1);
        list.add("a");
        list.add(book);
        
        int item1 = list.get(0);
        InputInt(item1);
        String item2 = list.get(1);
        InputString(item2);
        Book item3 = list.get(2);
        InputBook(item3);
        
        System.out.println(item1);
        System.out.println(item2);
        System.out.println(item3);

我们是可以这样写的,并且从结果来看也没有问题。上面我们为了验证list里面存的数据,因此使用System.out.println来打印到屏幕。代码里有三个方法,分别需要那三项数据作为参数传入,因此使用三个变量接收对应的数据项,再把这个变量作为参数传入方法就可以了。
可是写完了会发现,IDE报错了!查看提示,原来是需要强制类型转换:

		int item1 = (int) list.get(0);
        String item2 = (String) list.get(1);
        Book item3 = (Book) list.get(2);

可是这是为什么呢?刚刚在list中添加的数据,要获取的时候怎么还需要强制转换呢…哎呀,先不管那么多了,至少代码能运行,记住就是了。现在来出一个问题考考你:新建一个list,存入一个Book类型,然后请你帮我把它放到一个变量里并打印出来。你迫不及待地想要试一试自己究竟学会了没有,于是马上写了起来:

        List myList = new ArrayList();
        myList.add(book);
		//用变量接收者一项然后打印

刚刚写了这么多代码,你舍友突然喊你去吃饭,你想,吃完饭回来再写也不迟,于是给代码写上注释,就去吃饭了。等到回来再看注释,挠挠头,你也想不起来这个book是什么类型了,好像是String类型?试试吧:

		String data = (String) myList.get(0);
        System.out.println(data);

嗯,你写完代码运行,报错了!怎么会报错呢?写的时候明明没有问题呀?!你仔细看了看报错的信息,原来是类型转错了,然后回想起应该是要打印一个Book类型。你马上把代码改好:Book data = (Book) myList.get(0);这下没问题了。
细心的你应该已经发现,使用list的时候有一个隐患,那就是在使用一个和list中项不一致的数据类型来接收它的时候,IDE并没有给出错误提示。如果发生先刚才那样忘记了原本的类型,或者是其他人来接手你的代码,很容易使得程序在写的时候没错,运行起来却出错。怎么才能避免这种问题呢?答案很简单——使用泛型。
现在我们将刚才的任务使用泛型来写:

        List<Book> myList = new ArrayList<>();
        myList.add(book);
        String data = (String) myList.get(0);   //IDE会报错
        Book mydata = myList.get(0);    //不必再进行类型转换
        System.out.println(mydata);

这时候你发现,再使用String来接收的话,IDE就会提示你错误,而不必等到程序运行的时候再发现问题了。并且,使用泛型后再接收项的时候也不必再进行强制类型转换了。说了这么多,到底什么是泛型呢?你看第一行代码那种带尖括号的写法,那就是泛型。前面我们说过,一个List可以存放许多种不同数据类型的对象,但是大多时候我们只想用它来存放相同类型的数据。这时候在<>中写上你要存放的数据类型,这就代表这个List只能存放该种类型的数据,当你试图存放其他类型数据或者用其他类型数据接收时,IDE就能在编译期给出错误提示。这样也就避免了由于我们的写法的失误而导致错误被隐藏起来(程序运行时才会出错)。
现在你应该明白泛型为什么被称为语法糖了吧?那么,为什么Java中的泛型被称为伪泛型呢?
Java泛型之所以被称为伪泛型,是因为它的实现方式是类型擦除。我们使用反编译工具反编译一下一段代码观察一下,反编译之前:

	public static void main(String[] args){
        List<String> myList = new ArrayList<>();
        myList.add("泛型");
        System.out.println(myList.get(0));
    }

经过反编译发现,原来代码中的泛型都不见了,变成了使用泛型之前的样子:

public static void main(String[] args){
        List myList = new ArrayList();
        myList.add("泛型");
        System.out.println((String) myList.get(0));
    }

虽然我们写出来的程序代码中使用的泛型,凡是经过反编译后就被替换成原来的类型了,并且在相应的地方加入了强制类型转换。与之相对的C#在编译后,List没有被替换成List,而是是真实存在的,这就是为什么Java泛型被称为伪泛型。

泛型的本质是参数化类型的应用,使用泛型可以帮助你在写代码的编译期发现错误,而不是程序运行时,并且提高了代码的可读性

相信你对泛型已经基本了解了,想要知道更多泛型的知识就要靠大家自己探索了!

猜你喜欢

转载自blog.csdn.net/qq_42893430/article/details/103099710