唬人的Java泛型并不难
泛型
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
上面的代码有什么区别?
泛型初探
1、为何引入泛型?
Java 泛型也是一种语法糖,使用泛型可以在代码编译阶段完成类型的转换,避免代码在运行时强制转换而出现ClassCastException的异常。
网络搜索出来一大堆的名称解释,我们先看英文Generic type,从英文大概也能明白,Generic 这里可以理解为普通的,一般的,或者我们可以说通用的。
其实可以理解为Java中的一种类型,通用类型。
Java从1.5的版本就开始支持泛型,不过很多小伙伴对泛型还是模凌两可,今天大概讲讲泛型,基础好的小伙伴,就当复习复习。
在1.5版本以前
public static void main(String[] args){
List list = new ArrayList();
list.add("兔子托尼啊");
list.add(1234);
//正常运行
System.out.println((String)list.get(0));
//❌运行时报错
System.out.println((String)list.get(1));
}
从上面的代码可以看出了,第一句打印不报错,第二句打印会报错的。
List默认是Object的类型的,向List里面存数据都是没有问题的,但是取数据的时候,必须要要进行类型的转换。
List集合get数据的时候并不清楚里面存放的什么数据类型,默认取出来的都是Object的类型,如果取数据的时候转换的类型和原始存放存的类型不一样,会报ClassCastException的异常。
2、引入了泛型
看代码
List<String> list = new ArrayList<String>();
list.add("兔子托尼啊");
//❌编译时错误
list.add(1234);
//不需要再进行转换了
String str = list.get(0);
3、泛型带来好处
这在编码的时候就给我们解决了,类型转换的问题,可以放心写代码。
取数据的时候再也不要考虑我前面存的什么类型,我应该转换为什么类型,不怕类型转换报错。
类型擦除
上面讲了泛型,泛型虽然带来了好处,但是泛型也带了一个问题叫做类型擦除。
什么是类型擦除?
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。
Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
class GenericU {
public void foo() {
System.out.println("GenericU.foo()");
}
}
public class Operater<T> {
private T obj;
public Operater(T obj) {
this.obj = obj;
}
public void doIt() {
//❌报错,提示找不到foo方法
obj.foo();
}
public static void main(String[] args) {
GenericU genericU = new GenericU();
Operater<GenericU> operater = new Operater<>(genericU);
operater.doIt();
}
}
上面的代码就是因为泛型擦除,带来编译就报错了,代码中的obj不知道是什么类型?
正确的代码应该是什么,只要指定T的类型就好
class Operater2<T extends GenericU> {
private T obj;
public Operater2(T obj) {
this.obj = obj;
}
public void doIt() {
//正确☑️
obj.foo();
}
}
区分在Operater2<T extends GenericU>
和Operater<T>
必须指定泛型的类型。
上面的例子是运用在类上面的,方法中是什么效果呢?
class Foo{
//定义泛型方法..
public <T> void show(T t) {
System.out.println(t);
}
}
调用方法
public static void main(String[] args) {
//创建Foo对象
Foo foo = new Foo();
//不同的类型参数
foo.show("兔子托尼啊");
foo.show(1234);
foo.show(12.34);
}
通配符与上下界
我们大家在java的源码中肯定看到这样的例子。一个下限,一个上限
? extends T
VS ? super T
- ? extends T - 这里的?表示类型T的任意子类型,包含类型T本身。
- ? super T - 这里的?表示类型T的任意父类型,包含类型T本身。
上限通配符 可以代表未知的T类型,或者通过关键字 extends 所继承的T类的任何一个子类。
同样,下限通配符 可以代表未知的T类型,或者通过关键字super出来的的T类的任何一个父类。
通配符和泛型方法
//通配符
public void foo1(List<?> list) {
}
//使用泛型方法
public <T> void foo2(List<T> t) {
}
问: 上面两种代码都是可以的,但是什么场合用那种呢?
- 如果当参数之间有依赖关系,或者返回的参数有依赖关系则用泛型,反之则用通配符。
问:关于 ? extends T
和 ? super T
什么场景下用呢?
我从网上搜索了下
当你需要从一个数据结构中获取数据时(get),那么就使用 ? extends T;如果你需要存储数据(put)到一个数据结构时,那么就使用 ? super T; 如果你又想存储数据,又想获取数据,那么就不要使用通配符 ? ,即直接使用具体泛型T。
最后
泛型大概就讲了上面的内容,你看明白了吗?希望你又学到了,每天学一点,进步一点。升职加薪就是你了。
码字不易,关注后送福利,求关注。
唬人的Java泛型并不难的更多相关文章
- Java泛型的历史
为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...
- 浅析Java 泛型
泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...
- Java:泛型基础
泛型 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多! 解决这种限制的 ...
- java泛型基础
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法. Ja ...
- 使用java泛型设计通用方法
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...
- 关于Java泛型的使用
在目前我遇到的java项目中,泛型应用的最多的就属集合了.当要从数据库取出多个对象或者说是多条记录时,往往都要使用集合,那么为什么这么使用,或者使用时有什么要注意的地方,请关注以下内容. 感谢Wind ...
- 初识java泛型
1 协变数组类型(covariant array type) 数组的协变性: if A IS-A B then A[] IS-A B[] 也就是说,java中的数组兼容,一个类型的数组兼容他的子类类型 ...
- 【Java心得总结四】Java泛型下——万恶的擦除
一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...
- 【Java心得总结三】Java泛型上——初识泛型
一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...
随机推荐
- while循环计算1-100和,1-100内偶数/奇数/被整除的数的和
文章地址 https://www.cnblogs.com/sandraryan/ <!DOCTYPE html> <html lang="en"> < ...
- 深入java面向对象三:抽象类和接口(转载)
文章系转载,地址: http://blog.csdn.net/xw13106209/article/details/6923556 1.概述 一个软件设计的好坏,我想很大程度上取决于它的整体架 ...
- mysql 第一课 win7艰难的配置与删除注册表重新安装
mysql 这个安装一定要慎重!慎重!慎重! 重要的事情说三遍.. 不会的一定要按步骤来~出错了真的很难受. 详细教程 --> https://jingyan.baidu.com/album/ ...
- python模块之random模块
random模块 随机模块,用于处理随机问题. import random # 随机整数 print(random.randint(0, 9)) # 0到9之间随机一个整数 print(random. ...
- ES6 set和map数据结构对对象数组去重简单实现
自从有了es6的set数据结构,数组的去重可以简单用一行代码实现,比如下面的方式 let arr = [1, 2, 2, 3, 4] function unique (arr) { return [. ...
- linux scull 代码read 方法
read 的返回值由调用的应用程序解释: 如果这个值等于传递给 read 系统调用的 count 参数, 请求的字节数已经被传送. 这是最好的情况. 如果是正数, 但是小于 count, 只有部分数据 ...
- 1471 - Defense Lines
After the last war devastated your country, you - as the king of the land of Ardenia - decided it wa ...
- java用普通类如何实现枚举功能
用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能. 1.私有的构造方法. 2.每个元素分别用一个公有的静态成员变量表示. 可以有若干公有方法或抽象方法.采用 ...
- WPF 使用 Composition API 做高性能渲染
在 WPF 中很多小伙伴都会遇到渲染性能的问题,虽然 WPF 的渲染可以甩浏览器渲染几条街,但是还是支持不了游戏级的渲染.在 WPF 使用的 DX 只是优化等级为 9 和 DX 9 差不多的性能,微软 ...
- cookie小总结
对cookie总结 cookie 在服务器的环境下,对数据的本地存储下面为一个小小的案例 let d= new Date; d.setDate(d.getDate()+7); document.coo ...