关于java泛型你应该知道的那些事儿

news/2024/10/18 9:17:38/

泛型的设计初衷:是为了减少类型转换错误产生的安全隐患,而不是为了实现任意化。

泛型可以应用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法

  • 泛型类在类名后面用尖括号表示泛型
public class HelloWorld<T> {private T t;public T getValue() {return t;}public void setValue(T t) {this.t = t;}
}
  • 泛型方法在方法声明中加入泛型
//这是泛型方法
static <T> void printHelloWorld(T t){LOGGER.info(t);
}
//这是一个普通方法
public T getT() {return t;
}
  • 泛型接口跟类类似
public interface Generator<T> {public T next();
}

总结:

  • 有 带尖括号的 才能表示是泛型类或方法或接口。而只有T的只能表示是泛型类型

  • 泛型类型的命名并不是必须为T,也可以使用其他字母,如X、K等,只要是命名为单个大写字即可。

  • 虽然没有强制的命名规范,但是为了便于代码阅读,也形成了一些约定俗成的命名规范,如下:

T通用泛型类型,通常作为第一个泛型类型
S

通用泛型类型,如果需要使用多个泛型类型,可以将S作为第二个泛型类型

U通用泛型类型,如果需要使用多个泛型类型,可以将U作为第三个泛型类型
V通用泛型类型,如果需要使用多个泛型类型,可以将V作为第四个泛型类型
E集合元素 泛型类型,主要用于定义集合泛型类型
K映射-键 泛型类型,主要用于定义映射泛型类型
V映射-值 泛型类型,主要用于定义映射泛型类型
N数值 泛型类型,主要用于定义数值类型的泛型类型

通配符、上下边界、无界

如果不对泛型类型做限制,则泛型类型可以实例化成任意类型,这种情况可能产生某些安全性隐患。

为了限制允许实例化的泛型类型,我们需要一种能够限制泛型类型的手段,即:有界泛型类型

// 有界泛型类型语法 - 继承自某父类
<T extends ClassA>//有界泛型类型语法 - 实现某接口
<T extends InterfaceB>//有界泛型类型语法 - 多重边界
<T extends ClassA & InterfaceB & InterfaceC ... >//示例
//N标识一个泛型类型,其类型只能是Number抽象类的子类
<N extends Number> 
//T标识一个泛型类型,其类型只能是Person类型的子类,并且实现了Comparable 和 Map接口
<T extends Person & Comparable & Map>

上边界通配符(<? extends 父类型>)

  • 上界:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

  • 类型参数列表中如果有多个类型参数上限,用逗号分开

    private <K extends A, E extends B> E test(K arg1, E arg2){}
    
  • 上边界类型通配符可以确定父类型

  • 获取数据时,由于固定了上界,类型肯定是返回类型的子类型,可以通过向上转型成功获取。

  • 写入数据时,由于向下转型存在很大风险,Java泛型为了减低类型转换的安全隐患,不允许这种操作。

图片

Plate<? extends Fruit> p=new Plate<Apple>(new Apple());//不能存入任何元素
p.set(new Fruit());    //Error
p.set(new Apple());    //Error//读取出来的东西只能存放在Fruit或它的基类里。
Fruit newFruit1=p.get();
Object newFruit2=p.get();
Apple newFruit3=p.get();    //Error

原因是编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在看到后面用Plate赋值以后,盘子里没有被标上有“苹果”。而是标上一个占位符:CAP#1,来表示捕获一个Fruit或Fruit的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。

下边界通配符(<? super 子类型>)

  • 下界通配符<? super T>不影响往里面存储,但是读取出来的数据只能是Object类型。

  • 和上界相对的就是下界 ,语法表示为:<? super T>

图片

Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());//存入元素正常
p.set(new Fruit());
p.set(new Apple());//读取出来的东西只能存放在Object类里。
Apple newFruit3=p.get();    //Error
Fruit newFruit1=p.get();    //Error
Object newFruit2=p.get();

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

  1. 频繁往外读取内容的,适合用上界Extends。

  2. 经常往里插入的,适合用下界Super。

无边界通配符(<?>)

无边界通配符<?> 等同于上边界通配符<? extends Object>,所以关于无边界通配符(<?>)就很好理解了。

因为可以确定父类型是Object,所以可以以Object去获取数据(向上转型)。但是不能写入数据。

与<?>的区别

?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 :

// 可以
T t = operate();
// 不可以
?car = operate();

T 是一个 确定的类型,通常用于泛型类和泛型方法的定义

?是一个 不确定的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

类型参数 T 只具有 一种 类型限定方式:

T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A     

T 可以多重限定 而 ? 不行

<T extends ClassA & InterfaceB & InterfaceC ... >

泛型使用的限制

泛型不能使用基本类型

// 编译前类型检查报错
List<int> list = new List<int>();

泛型不允许进行实例化

<T> void test(T t){//编译前类型检查报错t = new T();
}

泛型不允许进行静态化

static class Demo<T>{// 编译前类型检查报错private static T t;// 编译前类型检查报错public static T getT() {return t;}}

泛型不允许直接进行类型转换(通配符可以)

List<Integer> integerList = new ArrayList<Integer>();
List<Double> doubleList = new ArrayList<Double>();
//不能直接进行类型转换,类型检查报错
//integerList = doubleList;

泛型不允许直接使用instanceof运算符进行运行时类型检查(通配符可以)

List<String> stringList = new ArrayList<String >();
//不能直接使用instanceof,类型检查报错
//LOGGER.info(stringList instanceof ArrayList<Double>);
//我们可以通过通配符的方式进行instanceof运行期检查(不建议):
//通过通配符实现运行时验证
LOGGER.info(stringList instanceof ArrayList<?>);

泛型不允许创建确切类型的泛型数组(通配符可以)

//类型检查报错
//Demo6<Integer>[] iDemo6s = new Demo6<Integer>[2];

泛型不允许定义泛型异常类或者catch异常(throws可以)

  • 不允许定义泛型异常类(直接或间接扩展Throwable类)

  • 不允许捕获一个泛型异常

  • 可以以异常类作为边界

  • 可以throws泛型类型

泛型不允许作为参数进行重载

static class Demo8<T>{void test(List<Integer> list){}//不允许作为参数列表进行重载//void test(List<Double> list){}
}

参考 :

  • https://blog.csdn.net/hanchao5272/article/details/79346471

  • https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super

  • https://guofeng007.com/2018/03/01/java-super-extends-template/#%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%8B%E7%95%8C

  • https://blog.csdn.net/hanchao5272/article/details/79352321

图片

关注公众号 获取更多精彩内容


http://www.ppmy.cn/news/755050.html

相关文章

iphone 如何运行android,如何在iPhone上运行Android双系统?

如果能够让一部 iPhone 实现系统分身并且各方面都更加完美那该多好&#xff0c;而海马玩推出的机甲就是这么一款高端智能外设&#xff0c;它不仅可以让你同时使用 iOS 和 Android 系统&#xff0c;还以组合创新的方式让iPhone各方面都得到了提升&#xff0c;那么现在我们不妨边…

java的若干问题(1)——继承、多态、抽象类与接口

今天&#xff0c;我们介绍一些关于继承、多态、抽象类与接口之间的一些问题与困惑。 1、继承的概念。 继承的关键字extends(扩展) 继承只允许多层继承&#xff0c;不能多重继承。&#xff08;C可以多重继承&#xff09; 继承中&#xff0c;父类定义无论是属性还是方法&#x…

苹果股票评级遭投行罕见下调 新产品已推不动股价上涨

据外电报道&#xff0c;尽管苹果在周一的全球开发者大会(WWDC)中发布了包括智能音箱HomePod在内的一系列新硬件产品和iOS 11等软件和服务&#xff0c;但由于一位投行分析师罕见的下调该公司股票评级&#xff0c;导致该公司股价见光死&#xff0c;未能随利好消息上涨。 投行Paci…

苹果用户:你被“霸权”政策刺伤过吗?

身为苹果的铁杆支持者&#xff0c;我买过很多苹果设备。虽然相对来说&#xff0c;苹果设备的价格相较同类产品较高&#xff0c;但正所谓物有所值&#xff0c;其带来的畅快体验也是其他设备无法媲美的。尤其是iPhone、iPad、Macbook组成的“三叉戟”&#xff0c;让我体验到很多乐…

苹果在华遭遇断崖式下滑,是谁搞的?

美国苹果公司的光芒正在褪去。过去一年&#xff0c;用“糟糕透顶”来形容这家公司的表现绝不为过&#xff0c;尤其是中国市场的急速下滑&#xff0c;对苹果而言可以说是巨大的打击。 据苹果公司最新公布的第三季度财报显示&#xff0c;其营收424亿美元&#xff0c;低于去年同期…

苹果6s解除耳机模式_三星,苹果最好的损友!从3GS损到iPhone 12从不留情|三星|iphone|手机|新iphone...

几乎每年&#xff0c;苹果的新机发布过后&#xff0c;都会有个小精灵鬼准时上线&#xff0c;给予无情吐槽。即使是苹果这样好脾气的谦谦君子&#xff0c;年年被这么怼&#xff0c;一时半会估计也是气得有话说不出&#xff0c;呛得半死。今年也不例外&#xff0c;iPhone 12系列新…

代码随想录算法训练营第58天 | 739.每日温度 + 496.下一个更大元素 I

今日任务 目录 739.每日温度 - Medium 496.下一个更大元素 I - Easy 739.每日温度 - Medium 题目链接&#xff1a;力扣-739. 每日温度 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i…

如何发布插件到npm

首先 你需要注册一个npm账号 npm 网址&#xff1a;https://www.npmjs.com/ 点击 Sign in 跳转到登录页面 点击 Create Account 进行一个新建账户 注册完成后会有一封邮件发送一个一次性密码&#xff0c;到时候验证一下就行。 登录完成之后 点击你的头像 点击Account 进行验证…