欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > Java——泛型

Java——泛型

2025/2/22 16:31:53 来源:https://blog.csdn.net/2301_80867182/article/details/144355133  浏览:    关键词:Java——泛型

(!!!为了能够更好的学习容器,我们首先要先来学习一个概念:泛型!!!)

一 泛型简介

(泛型的本质——>数据类型的参数化)

(把“泛型”理解为数据类型的一个占位符(类似:形式参数))

(泛型的<数据类型> 只能是引用类型——>不能使用基本数据类型了,只能使用对象/引用类型)

(泛型主要是方便了程序员的代码编写,以及更好的安全性检测)

1 泛型基本概念

泛型是JDK5.0以后增加的新特性。

泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入。我们可以把“泛型”理解为数据类型的一个占位符(类似:形式参数),即告诉编译器,在调用泛型时必须传入实际类型

参数化类型,白话说就是:

  1. 把类型当作是参数一样传递。
  2. <数据类型> 只能是引用类型。

2 泛型的好处——>(在编译期就会报错)

在不使用泛型的情况下,我们可以使用Object类型来实现任意的参数类型,但是在使用时需要我们强制进行类型转换。这就要求程序员明确知道实际类型,不然可能引起类型转换错误;但是,在编译期我们无法识别这种错误,只能在运行期发现这种错误。使用泛型的好处就是可以在编译期就识别出这种错误,有了更好的安全性;同时,所有类型转换由编译器完成,在程序员看来都是自动转换的,提高了代码的可读性。

总结一下,就是使用泛型主要是两个好处:

  1. 代码可读性更好【不用强制转换】
  2. 程序更加安全【只要编译时期没有警告,运行时期就不会出现ClassCastException异常】

3 类型擦除

编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。

泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。

泛型主要是方便了程序员的代码编写,以及更好的安全性检测。

二 泛型类

(在类上定义泛型)
(public class 类名<泛型表示符号> {} , 或public class 类名<泛型表示符号,泛型表示符号>{})

(用了泛型如果类型不匹配编译期间会报错,规范了)

1 泛型标记

定义泛型时,一般采用几个标记:E、T、K、V、N、?。他们约定俗称的含义如下:

泛型标记对应单词说明
EElement在容器中使用,表示容器中的元素
TType表示普通的JAVA类
KKey表示键,例如:Map中的键Key
VValue表示值
NNumber表示数值类型
表示不确定的JAVA类型

2 泛型类的使用

语法结构

public class 类名<泛型标识符号> {
}public class 类名<泛型标识符号,泛型标识符号> {
}

示例

public class Generic<T> {private T flag;public void setFlag(T flag){this.flag = flag;}public T getFlag(){return this.flag;}
}

实例化时需要给定具体类型的,如果没有默认的object类型

public class Test {public static void main(String[] args) {//创建对象时,指定泛型具体类型Generic<String> generic = new Generic<>();generic.setFlag("admin");String flag = generic.getFlag();System.out.println(flag);//创建对象时,指定泛型具体类型Generic<Integer> generic1 = new Generic<>();generic1.setFlag(100);Integer flag1 = generic1.getFlag();System.out.println(flag1);}
}

在这里插入图片描述

用了泛型如果类型不匹配编译器会报错,规范了

三 泛型接口

(在接口上定义泛型)
(public interface 接口名<泛型表示符号>{} , public interface 接口名<泛型表示符号,泛型表示符号>{})

(实现时传递,使用时传递)

泛型接口的使用

语法结构

public interface 接口名<泛型标识符号> {
}public interface 接口名<泛型标识符号,泛型标识符号> {
}

示例

public interface IGeneric<T> {T getName(T name);
}
//1 在实现接口时传递具体数据类型
public class IgenericImpl implements Igeneric<String> {@Overridepublic String getName(String name) {return name;}
}//2 在实现接口时仍然使用泛型作为数据类型
public class IGenericImpl2<T> implements IGeneric<T>{@Overridepublic T getName(T name) {return name;}
}
public class Test {public static void main(String[] args) {IGeneric<String> igeneric= new IGenericImpl();String name = igeneric.getName("xiao");System.out.println(name);IGeneric<String> igeneric1 = new IGenericImpl2<>();String name1 = igeneric1.getName("jia");System.out.println(name1);}
}

四 泛型方法

(在方法上定义泛型)
(无返回值方法public<泛型表示符号>void getName(泛型标识符号name){})
(有返回值方法public<泛型表示符号>泛型表示符号getName(泛型标识符号namme){})

(类上定义的泛型,在方法中也可以使用。但有时需要仅仅在某一个方法上使用泛型,这时候可以使用泛型方法)

(调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型)

(静态方法只能在方法上定义泛型)

泛型方法的使用

1 非静态方法

非静态方法可以使用泛型类中所定义的泛型,也可以将泛型定义在方法上。

语法结构

//无返回值方法
public <泛型标识符号> void getName(泛型标识符号 name){
}//有返回值方法
public <泛型标识符号> 泛型标识符号 getName(泛型标识符号 name){
}

示例

public class MethodGeneric {public <T> void setName(T name){System.out.println(name);}public <T> T getAge(T age){//定义泛型和使用泛型return age;}
}
public class Test2 {public static void main(String[] args) {MethodGeneric methodGeneric = new MethodGeneric();methodGeneric.setName("oldlu");Integer age = methodGeneric.getAge(123);System.out.println(age);}

2 静态方法

静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型,所以必须要将泛型定义在方法上

语法结构

//无返回值静态方法
public static <泛型标识符号> void setName(泛型标识符号 name){
}//有返回值静态方法
public static <泛型标识符号> 泛型表示符号 getName(泛型标识符号 name){
}

示例

public class MethodGeneric {public static <T> void setFlag(T flag){System.out.println(flag);}public static <T> T getFlag(T flag){return flag;}
}
public class Test4 {public static void main(String[] args) {MethodGeneric.setFlag("oldlu");Integer flag1 = MethodGeneric.getFlag(123123);System.out.println(flag1);}
}

五 泛型方法与可变参数

(public<泛型表示符号> void showMsg(泛型标识符号… agrs){})

(在泛型方法中,泛型也可以定义可变参数类型)

(泛型标识符号… agrs)——>可变数组

语法结构

public <泛型标识符号> void showMsg(泛型标识符号... agrs){
}

示例

public class MethodGeneric {public <T> void method(T...args){for(T t:args){System.out.println(t);}}
}
//用for each遍历
MethodGeneric methodGeneric = new MethodGeneric();
String [] arr = new String[]{"a","b","c"};
Integer [] arr2 = new Integer[]{1,2,3,4,5};
methodGeneric.method(arr);
methodGeneric.method(arr2);
//两种数据都能操作

六 泛型中的通配符

(使用泛型的时候要用<?>,T之类的是定义泛型时使用的标识符)

(当类型不确定时可以用<?>)

无界通配符

“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。

语法结构

public void showFlag(Generic<?> generic){
}

示例

public class Generic<T> {private T flag;public void setFlag(T flag){this.flag = flag;}public T getFlag(){return this.flag;}
}
public class ShowMsg {public void showFlag(Generic<?> generic){System.out.println(generic.getFlag());}
}//如果<?>换成了Integer类型,那么下面的代码只能使用Integer类型
public class Test3 {public static void main(String[] args) {ShowMsg showMsg = new ShowMsg();Generic<Integer> generic = new Generic<>();generic.setFlag(20);showMsg.showFlag(generic);Generic<Number> generic1 = new Generic<>();generic1.setFlag(50);showMsg.showFlag(generic1);Generic<String> generic2 = new Generic<>();generic2.setFlag("jjj");showMsg.showFlag(generic2);}
}

七 统配符的上下限定

(!!!约束机制!!!——>比如继承了number就不能使用String类型了)

(上线限定——>实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型)

(下线限定——>实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型)

1 统配符的上限限定

在这里插入图片描述

对通配符的上限的限定:<? extends 类型>

?实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;

语法结构

public void showFlag(Generic<? extends Number> generic){
}

示例

public class ShowMsg {public void showFlag(Generic<? extends Number> generic){System.out.println(generic.getFlag());}
}
//规定了只能使用继承的类或者它的子类
public class Test4 {public static void main(String[] args) {ShowMsg showMsg = new ShowMsg();Generic<Integer> generic = new Generic<>();generic.setFlag(20);showMsg.showFlag(generic);Generic<Number> generic1 = new Generic<>();generic1.setFlag(50);showMsg.showFlag(generic1);}
}

2 通配符的下限限定
在这里插入图片描述

对通配符的下限的限定:<? super 类型>

?实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

语法结构

public void showFlag(Generic<? super Integer> generic){
}

示例

public class ShowMsg {public void showFlag(Generic<? super Integer> generic){System.out.println(generic.getFlag());}
}
//只能使用Integer类型或者它的父类,不能使用子类型
public class Test6 {public static void main(String[] args) {ShowMsg showMsg = new ShowMsg();Generic<Integer> generic = new Generic<>();generic.setFlag(20);showMsg.showFlag(generic);Generic<Number> generic1 = new Generic<>();generic1.setFlag(50);showMsg.showFlag(generic1);}
}

八 泛型总结

!!!!!泛型的作用!!!!!

实现数据类型参数化的——>为了解决(没有泛型的时候,在使用一些类型时,需要做一些强制类型转化,有可能出现类型不匹配的异常,这种异常是在代码运行时出现的,编译器不会出现异常)

泛型通过占位符的思想,对数据类型做了一个参数的定义,什么时候确切的使用泛型了,再将确切的类型传递进来,这样代码当中就不需要做强制类型转化了,而且一旦类型自己所给定的和它定义的不匹配,在编译器就会有异常提示,避免了在运行期间出现问题,相当于对数据处理的安全措施

泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息。 类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。因此,使用泛型时,如下几种情况是错误的:

  1. 基本类型不能用于泛型

    Test<int> t; 这样写法是错误,我们可以使用对应的包装类Test<Integer> t ;

  2. 不能通过类型参数创建对象

    T elm = new T(); 运行时类型参数T会被替换成Object,无法创建T类型的对象,容易引起误解,java干脆禁止这种写法。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词