目录
有许多的原因促进了泛型的出现,而最引人注目的原因,就是为了创造 容器类。容器:就是要存放使用对象的地方。数组也是如此,不过相比于简单的数组,容器更加的灵活,具备更多的能力,来实现更多的功能。
泛型概述
1,泛型是什么
泛型(Generics)是把数据类型参数化。是指在对象建立的时候,不指定类中属性的具体类型,而由外部在声明及其实例化对象时指定类型,可以把泛型理解成为数据类型的占位符,类似于形式参数
2,泛型的应用
泛型设置的目的就是为了解决对象强制向下转型所带来的安全隐患,而解决唯一思路就是不转型,即直接利用具体类型来进行操作。
3,泛型的好处
增强代码的可读性,并且泛型可以解决数据类型的安全性问题,使得程序更加安全
泛型类
定义
泛型类就是把泛型定义在类的上面,用户使用该类的时候,才会把类型确定下来,泛型类的具体使用方法是在类的名称后面添加一个或者多个类型参数声明
[访问权限] class 类名称<泛型类型标识1,泛型类型标识2,...,泛型类型标识3>{
[访问权限] 泛型类型标识 变量名称;
[访问权限] 泛型类型标识 方法名称(){};
[访问权限] 泛型类型标识 方法名称(泛型类型标识 变量名称){};
}
泛型对象定义
类名称<具体类> 对象名称 = new 类名称<具体类>();
泛型标识符可以是任意的,一般情况下使用以下标识符作为标记:
泛型标记 | 对应单词 | 说明 |
E | Element | 在容器中使用,表示容器中的元素 |
T | Type | 表示普通的JAVA类 |
K | Key | 表示键 |
V | Value | 表示值 |
N | Number | 表示数值类型 |
? | 表示不确定的JAVA类型 |
泛型的声明
class A<T> //此处可以是任意的标识符号,T是type的简称
{
private T var; //此变量的类型由外部决定
public T getVar(){ //返回值的类型由外部决定
return var;
}
public void setVar(T var) //设置的类型由外部决定
{
this.var = var;
}
}
结合对象定义和声明将var的类型设置为整数类型
public class DemoA
{
public static void main(String[] args) {
A<Integer> a = new A <Integer>();
a.setVar(666);
System.out.println(a.getVar());
}
}
//输出结果:666
泛型接口
1,格式
声明泛型接口和声明泛型的语法类似,也是在接口名称后面加上<泛型标记>
[访问权限] interface 接口名称<泛型标识>{
}
2, 泛型接口的两种实现方式
public interface Info<T>
{
T getName(T name);
}
class InfoImpl implements Info<String>{
@Override
public String getName(String name) {
return name;
}
}
public class Test
{
public static void main(String[] args) {
InfoImpl i1 = new InfoImpl(); //直接在接口中指定具体类型
String A = i1.getName("甲基橙");
System.out.println(A);
Info<String> i2 = new InfoImpl(); //在接口上面定义泛型
String B = i2.getName("橙子味热果汁");
System.out.println(B);
}
}
泛型方法
定义
泛型方法指的是将方法的参数类型定义成为泛型,以便于在调用时接收不同类型的参数,此时的参数类型就是传入数据的类型。类型参数可以有多个,用逗号进行隔开。
格式
[访问权限] <泛型标识> 泛型标识 方法名称([泛型标识 参数名称])
非静态方法中使用泛型
调用泛型方法的时候,不需要像泛型类那样告诉编译器是什么类型
如果设置了泛型类型的话,那么对象的泛型类型必须要一致,否则在编译的时候就会报错
public class Method {
public <T> void setName(T name){
System.out.println(name);
}
public <T> T getName(T name){
return name;
}
}
public class Test {
public static void main(String[] args) {
Method m1 = new Method(); //调用泛型方法的时候,不需要像泛型类那样告诉编译器是什么类型
m1.setName("甲基橙");
m1.setName(666);
Method m2 = new Method();
String A = m2.getName("橙子味热果汁");
Integer B = m2.getName(123);
System.out.println(A);
System.out.println(B);
}
}
静态方法使用泛型
说明:在静态方法当中,泛型是不允许使用类当中的泛型;如果是静态方法当中想使用泛型,我们只能在方法上通过泛型方法的方式来定义泛型,这是静态方法与非静态方法使用泛型的区别
public class Method {
public static <T> void setWay(T way){
System.out.println(way);
}
public static <T> T getWay(T way){
return way;
}
}
public class Demo {
public static void main(String[] args) {
//调用无返回值的setWay方法
Method.setWay("甲基橙");
Method.setWay(666);
//调用有返回值的getWay方法
String way1 = Method.getWay("橙子味热果汁");
Integer way2 = Method.getWay(123);
System.out.println(way1);
System.out.println(way2);
}
}
静态方法只能在泛型方法的方式使用泛型
非静态方法可以通过泛型类的泛型和泛型方法的方式使用泛型
泛型数组
使用泛型方法时,也可以传递或返回一个泛型数组
public class list {
public static void main(String[] args) {
Integer i[] = fun1(1,2,3,4,5,6,7,8,9); //返回泛型数组
fun2(i); //输出数组的内容
}
public static <T> T[] fun1(T ...arg){ //接收可变参数,返回泛型数组
return arg; //返回泛型数组
}
public static <T> void fun2(T Param[]){ //接收泛型数组
System.out.print("接收泛型数组:");
for (T t:Param){
System.out.print(t + ",");
}
System.out.println();
}
}
// 输出:接收泛型数组:1,2,3,4,5,6,7,8,9,
在此程序从fun1()方法返回一个泛型数组,在fun1()方法接收参数的时候,使用了可变参数的传递方式,然后将fun1()返回的泛型数组内容交给fun2()方法进行输出
泛型的嵌套设置
如果一个类中有多个属性需要使用不同的泛型声明,则可以在声明类的时候指定多个泛型的内容,然后也可以在一个类的泛型中指定另外一个泛型的内容
class Info<T,V> {
private T type;
private V value;
public Info(T type, V value) {
this.type = type;
this.value = value;
}
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
class Demo<S>{
private S info;
public Demo(S info) {
this.info = info;
}
public S getInfo() {
return info;
}
public void setInfo(S info) {
this.info = info;
}
}
/*
为 Info 类指定两个泛型类型,而 Demo 类也需要指定一个泛型类型,
在操作的时候可以将 Info 设置为Demo的泛型类型
*/
public class Test {
public static void main(String[] args) {
Info<String,Integer> i = new Info <>("橙子味热果汁",20); //info指定两个泛型类型
Demo<Info<String,Integer>> d = new Demo <>(i); //将定义的info作为Demo的泛型类型
System.out.print(d.getInfo().getType());
System.out.print(",");
System.out.print(d.getInfo().getValue());
}
//输出:橙子味热果汁,20
通配符
首先我们要了解通配符是什么?
问号" ? "代表通配符,只能在<>里面使用;它代表泛型中的未知类型,用于代替具体的类型
无界通配符
public class Info<T>
{
private T count;
public T getCount() {
return count;
}
public void setCount(T count) {
this.count = count;
}
}
public class Demo {
public void showCount(Info<Integer> info)
{
System.out.println(info.getCount());
}
}
public class Test {
public static void main(String[] args) {
Demo d = new Demo();
Info<Integer> i = new Info <>();
i.setCount(666);
d.showCount(i); //这里的i会正常输出:666
Info<Number> i2 = new Info <>();
i2.setCount(123);
d.showCount(i2); //这里面的i2会报错
}
}
当我Demo类里面的泛型变为<Number>的时候,那么我Test类里 i 的输出就会报错。尽管Integer是Number的子类,但是在进行引用传递的时候也同样无法进行操作,因为泛型它不会考虑到你的继承关系。为了解决这些方面的问题,java就引入了通配符" ? ",表示可以接收此类型的任意泛型对象
public class Demo {
public void showCount(Info<?> info) //把Integer改变为?
{
System.out.println(info.getCount());
}
}
有一点需要注意:在使用时,接收泛型对象的时候,就不能设置被泛型指定的内容了,就产生了一个错误
受限泛型
在引用传递的泛型操作中也可以设置一个泛型对象的范围上限和范围下限
通配符的上限限定
范围上限使用 extends 进行声明,表示泛型的类型可能是所指定的类型或者是此类型的子类
格式
声明对象:类名称<? extends 类> 对象名称
定义类:[访问权限] 类名称<泛型标识 extends 类>{}
应用
1,设置方法指定接收泛型的上限
在我的测试类里面 i2 的String类型就会报错,因为String类不是Number的子类,而 i 是Integer类,是Number的子类,所以i可以正常输出
public class Demo {
public void showCount(Info<? extends Number> info)
{
System.out.println(info.getCount());
}
}
2,在类定义的时候指定泛型的上限
public class Info<T extends Number> {
private T count;
public T getCount() {
return count;
}
public void setCount(T count) {
this.count = count;
}
}
public class Demo {
public static void main(String[] args) {
Info<Integer> i1 = new Info <>(); //这里的i1会正常进行
Info<String> i2 = new Info <>(); //这里会有报错
i1.setCount(123);
i2.setCount("甲基橙");
System.out.println(i1.getCount());
System.out.println(i2.getCount());
}
}
通配符的下限限定
范围下限使用 super 关键字进行声明,表示泛型的类型可能是所指定的类型,或者是此类型的父类型,或者是Object类型
格式
声明对象:类名称<? super 类> 对象名称
定义类:[访问权限] 类名称<泛型标识 extends 类>{}
应用
当使用的泛型只能在本类及其父类类型上应用的时候,就必须使用范围下限进行配置
public class Info<T> {
private T count;
public T getCount() {
return count;
}
public void setCount(T count) {
this.count = count;
}
@Override
public String toString() {
return "Info{" +
"count=" + count +
'}';
}
}
public class Test {
public static void main(String[] args) {
Info<String> i1 = new Info <>();
Info<Object> i2 = new Info <>();
i1.setCount("甲基橙");
i2.setCount("橙子味热果汁");
fun(i1);
fun(i2);
}
public static void fun(Info<? super String> temp){
System.out.println(temp);
}
}
在使用fun()方法的时候,Info进行了下限的配置,所以只能接收泛型是String和Object类型的引用,如果传递其他泛型类型的对象,就会报错