一、泛型介绍:
泛型可以用来: 在一个类或接口的声明处指定该类中某个属性的类型或方法返回值的类型或方法参数的类型
JDK5除了推出foreach新循环,还推出了一个新特性:泛型
泛型也称为参数化类型,它允许我们在一个类或接口的声明处指定该类中某个属性的类型或
方法返回值的类型或方法参数的类型,使得我们使用这个类时更方便更灵活。
使用了泛型的类叫泛型类、 使用了泛型的接口叫泛型接口、 使用了泛型的方法叫泛型方法
泛型在集合中广泛使用,用于指定该集合中的元素类型。
注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
用泛型的好处: 避免了类型强转的麻烦, 在编译期就做了类型检查,避免了在运行时出现ClassCastException。
注意: 泛型具体的类型不能为8种基本类型
注意: 泛型只在编译阶段有效(检查数据类型)正确检验泛型后,会将泛型的相关信息擦除。
上述结论可通过下面反射的例子来印证: (因为绕过了编译阶段也就绕过了泛型,输出结果为:[zyq, 100])
ArrayList<String> a = new ArrayList<String>();
a.add("zyq");
Class c = a.getClass();
try{
Method method = c.getMethod("add",Object.class);
method.invoke(a,100);
}catch(Exception e){
e.printStackTrace();
}System.out.println(a);
二、泛型用在集合中:
package com.zyq.se.javaGenerics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @author zhaoYQ
* JDK5除了推出foreach新循环,还推出了一个新特性:泛型
* * 泛型也称为参数化类型,它允许我们在一个类或接口的声明处指定该类中某个属性的类型或
* * 方法返回值的类型或方法参数的类型,使得我们使用这个类时更方便更灵活。
* 使用了泛型的类叫泛型类、 使用了泛型的接口叫泛型接口、 使用了泛型的方法叫泛型方法
* * 泛型在集合中广泛使用,用于指定该集合中的元素类型。
* 注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
* 用泛型的好处: 避免了类型强转的麻烦, 在编译期就做了类型检查,避免了在运行时出现ClassCastException。
*
* 注意: 泛型只在编译阶段有效(检查数据类型)正确检验泛型后,会将泛型的相关信息擦除。
* 上述结论可通过下面反射的例子来印证: (因为绕过了编译阶段也就绕过了泛型,输出结果为:[zyq, 100])
* ArrayList<String> a = new ArrayList<String>();
* a.add("zyq");
* Class c = a.getClass();
* try{
* Method method = c.getMethod("add",Object.class);
* method.invoke(a,100);
*
* }catch(Exception e){
* e.printStackTrace();
* }System.out.println(a);
* 注意: 泛型具体的类型不能为8种基本类型
*/
public class Demo1_CollectionGenericity {
public static void main(String[] args) {
/*
1.interface Collection<E>接口在定义处,指定了一个泛型E。
我们在实际使用集合时可以指定E的实际类型。
这样一来,编译器会检查集合中的元素和泛型规定的类型是否匹配。
例如,集合的方法:
boolean add(E e)
编译器会检查我们调用add方法向集合中添加元素时,元素的类型是否为E指定的
类型,不符合编译不通过。
*/
//集合创建的new后边的泛型可以省略 (但是尖括号不能省略)
Collection<String> c = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
//c.add(123);//不符合E在这里的实际类型String,因此编译不通过。
/*
* 2.使用新循环遍历时,如果集合指定了泛型,那么接收元素时
* 可以直接用对应的类型接收元素。
*/
//新循环遍历集合时,底层会自动改回成迭代器遍历
for(String s : c){
String x=s+"。";
System.out.println(x);
}
//3.迭代器也支持泛型,指定时要与其遍历的集合指定的泛型一致。
Iterator<String> e = c.iterator();
while(e.hasNext()){
String str = e.next();
System.out.println(str);
}
}
}
三、泛型用在类上
-
泛型类可以用于: 规定成员变量的类型, 方法参数类型 , 返回值类型
-
在定义类时使用了泛型, 需要在创建对象的时候确定泛型:
-
(jdk1.7后右边的泛型可以省略<第二个泛型括号内容可以省略new对象时第二个泛型具体的类型>)
泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。
- 案例:
package com.zyq.se.javaGenerics;
/**
* @author zhaoYQ
* 泛型类的演示
* 使用泛型类定义的类型 : 规定成员变量的类型, 方法参数类型 , 返回值类型
*/
public class Demo2_GenericityClass {
public static void main(String[] args) {
//1.在创建对象的时候确定泛型: 创建Person对象时需要确定R的类型
// (jdk1.7后右边的泛型可以省略<第二个泛型括号内容可以省略>)
//泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。
Person<String> p=new Person<String>();
//这里传入<String>的意思相当于将字符R替换为了String。 效果如下:
/*
class Person<String>{
private String gender;
public void setGender(String e){
this.gender=e;
}
//泛型方法getE的返回值类型为R,
// R的类型由外部指定(当创建对象时确定R的类型)
public String getGender(){
return gender;
}
}
*/
//调用泛型方法setE()给p对象的gender属性赋值
p.setGender("男");
//测试获取gender属性值
System.out.println(p.getGender());
//调用泛型方法getE()获取p对象的gender属性值
String rs=p.getGender();
//测试获取gender属性值
System.out.println("rs = " + rs);
//2.泛型类中表示类型的R可以不传入实际的类型(泛型实参)
//在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,
// 此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,
// 在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
//(但是在编程时容易出现类型转化异常)
Person pe=new Person();
pe.setGender(444);
}
}
/**
* @author zhaoYQ
* 类定义上加泛型,可以用任意一个字母代表类型,当
* 创建此类对象 时需要给此字母传入具体的类型
* (此字母一般习惯上大家都会用T或者E<一般不会随便写>)
* @param <R>
*/
class Person<R>{
private R gender;
//泛型方法形参t的类型也为R,
// R的类型由外部指定(用对象调用此方法传参时确定)
public void setGender(R e){
this.gender=e;
}
//泛型方法getE的返回值类型为R,
// R的类型由外部指定(当创建对象时确定R的类型)
public R getGender(){
return gender;
}
}
四、泛型方法:
- 使用泛型方法声明的类型 : 方法参数类型 , 返回值类型
- 案例:
package com.zyq.se.javaGenerics;
/**
* @author zhaoYQ
* 泛型方法的演示
* 使用泛型方法声明的类型 : 方法参数类型 , 返回值类型
*/
public class Demo3_GenericityMethod {
/**
* <T> 用来规定泛型类型(固定一个类型)在调用方法时确定具体的类型
* <T>后的T表示方法的返回值类型(为方法调用时规定的类型T)
* T parameter 表示方法的参数类型也采用了T类型(具体T的类型在调用方法时确定)
* @param parameter 方法参数
* @param <T> 用来规定泛型类型(固定一个类型)
* @return 一个T类型的数据
*/
public <T>T getData(T parameter){
return parameter;
}
public <T>String getData2(T parameter){
return parameter.toString();
}
public static void main(String[] args) {
Demo3_GenericityMethod generiMethodDemo=new Demo3_GenericityMethod();
//这里就在调用方法时,在getData()括号中传入了参数,同时据此可以确定T的类型为String类型
//(因为getData方法传入的数据的类型为String类型的对象,所以T就是String类型了)
//因为知道方法的返回值类型为String(和方法参数类型相同), 所以可以用String变量接收结果
String rs=generiMethodDemo.getData(new String("hello"));
//注意泛型方法一般方法返回值类型为T,方法参数类型也会是T(因为要根据方法参数确定返回值类型)
}
}
五、泛型接口
- 接口在定义时可以使用泛型
- 实现一个使用了泛型接口 : 需要在子类定义处声明类型, 或者在对象创建时声明类型
- 案例:
package com.zyq.se.javaGenerics;
/**
* @author zhaoYQ
* 泛型接口的演示
* 实现一个使用了泛型接口 :
* 在子类定义处声明类型, 或者在对象创建时声明类型
*/
public class Demo4_GenericityInterface {
public static void main(String[] args) {
//1.实现接口时确定T的类型
ToyCar car=new ToyCar();
String carRs=car.play("'金色童年卡丁车场地赛车场'");
System.out.println("carRs = " + carRs);
//2.实现接口时不确定T的类型(需要在创建对象时确定类型)
ToyBarbie<Character> Barbie=new ToyBarbie<Character>();
Barbie.play('红');
}
}
/**
* 玩具接口
* @param <T>
*/
interface Toy<T>{
T play(T msg);
}
/**
* 玩具汽车实现玩具接口 (实现接口时确定T的类型)
* 玩具汽车ToyCar在实现接口时确定T的类型为String
* 则play方法的参数类型和返回值类型也就会为String
*/
class ToyCar implements Toy<String>{
/**
* 玩玩具汽车的方法
* @param place 地点
* @return 一段文字
*/
public String play(String place) {
return "在"+place+"玩具汽车";
}
}
/**
* 玩具芭比娃娃实现玩具接口
* 玩具汽车ToyCar在实现接口时确定T的类型为String
* 则play方法的参数类型和返回值类型也就会为String
*/
class ToyBarbie<T> implements Toy<T>{
/**
* 玩玩具汽车的方法
* @param place 地点
* @return 一段文字
*/
public T play(T place) {
System.out.println("Barbie= 妍妍在玩'"+place+"'色的芭比娃娃");
return place;
}
}
六、泛型通配符:
泛型通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
package com.zyq.se.javaGenerics;
import java.util.ArrayList;
/**
* 泛型通配符:
* 不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
*/
public class Demo5_GenericityWildcard {
public static void main(String[] args) {
//1.集合中使用 ? 作为泛型时不能添加元素(所以集合基本不用?)
ArrayList<?> list1 = new ArrayList<Object>();
//list1.add(1);
//list1.add("r");
//2.泛型通配符?主要应用在参数传递方面
ArrayList<String> list2 = new ArrayList<String>();
list2.add("张");
list2.add("王");
testAdd(list2);
}
/**
* 2.泛型通配符?主要应用在参数传递方面
*/
public static void testAdd(ArrayList<?> eles){
for (Object o: eles) {
System.out.println(o);
}//在方法中是不能操作集合元素的,因为集合元素类型是?类型(未知类型<不是Object类型>)
}
}
七、泛型高级用法:
- 泛型高级用法: 用?规定一个泛型的上限和下限。
1.泛型的上限:
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
2.泛型的下限:
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
- 案例:
package com.zyq.se.javaGenerics;
import java.util.ArrayList;
import java.util.Collection;
/**
* author: zhaoYQ
* 泛型高级用法
* 用?规定一个泛型的上限和下限。
* 1.泛型的上限:
* 格式: 类型名称 <? extends 类 > 对象名称
* 意义: 只能接收该类型及其子类
*
* 2.泛型的下限:
* 格式: 类型名称 <? super 类 > 对象名称
* 意义: 只能接收该类型及其父类型
*/
public class Demo6_GenericityAnvanced {
public static void main(String[] args) {
//1.泛型的上限<? extends 类1 >: 泛型的最顶级是类1(不能是类1的父类<可以是类1或类1的子类>)
//ArrayList<? extends Animal> list = new ArrayList<Object>();//报错
//上边报错原因是: 泛型的最顶级是Animal(泛型需要是Animal或者是Animal的子类)(不能是Animal的父类)
ArrayList<? extends Animal> list2 = new ArrayList<Animal>();//泛型可以是Animal
ArrayList<? extends Animal> list3 = new ArrayList<Dog>();//泛型可以是Animal的子类
ArrayList<? extends Animal> list4 = new ArrayList<Cat>();//泛型可以是Animal的子类
//2.泛型的下限<? super 类2 >: 泛型的最底层是类2(不能是类2的子类<可以是类2或类2的父类>)
ArrayList<? super Animal> list5 = new ArrayList<Object>();//泛型可以是Animal的子类
ArrayList<? super Animal> list6 = new ArrayList<Animal>();//泛型可以是Animal类
//ArrayList<? super Animal> list7 = new ArrayList<Dog>();//报错
//上边报错原因是: 泛型的最底层是Animal(泛型需要是Animal或者是Animal的父类)
//ArrayList<? super Animal> list8 = new ArrayList<Cat>();//报错
//上边报错原因是: 泛型的最底层是Animal(泛型需要是Animal或者是Animal的父类)
//3.一般泛型的上限和下限也是用来进行方法参数传递的(具体看)
Collection<Integer> lis1 = new ArrayList<Integer>();
Collection<String> lis2 = new ArrayList<String>();
Collection<Number> lis3 = new ArrayList<Number>();
Collection<Object> lis4 = new ArrayList<Object>();
System.out.println(getElement1(lis1));
//报错(因为方法中规定了传入的集合元素类型必须是Number类或是Number的子类<String不是>)
//System.out.println(getElement1(lis2));//报错
System.out.println(getElement1(lis3));
//报错(因为方法中规定了传入的集合元素类型必须是Number类或是Number的子类<Object不是>)
//System.out.println(getElement1(lis4));//报错
//报错(因为方法中规定了传入的集合元素类型必须是Number类或是Number的父类<Integer不是>)
//System.out.println(getElement2(lis1));//报错
//报错(因为方法中规定了传入的集合元素类型必须是Number类或是Number的父类<String不是>)
//System.out.println(getElement2(lis2));//报错
getElement2(lis3);
getElement2(lis4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static Object getElement1(Collection<? extends Number> list){
return list.toArray()[0];
}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static Object getElement2(Collection<? super Number> list){
return list.toArray()[0];
}
}
class Animal{}//父类
class Dog extends Animal{}//子类
class Cat extends Animal{}//子类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术