泛型简介
在之前关于类的学习中我们知道,一个类中可以定义它的属性以及方法,在那里我们定义类的属性时不同的属性我们采用的是不同的数据类型,这就要求我们对每一个数据的类型进行声明操作。但是我们想到这样一个问题,如果这个类有无数个属性的时候我们怎么对这个类的属性进行定义呢?显然,将这个类的所有属性进行枚举型的定义是不现实的,因此这就要求我们找到属性定义的共同特性。这时Object出现在了我们面前,用这个关键字可以对不同类型的属性进行定义接收,但是这样我们也不知道具体的属性的类型,在涉及类型转换时也很容易出现错误,并且这钟错误一般出现在运行阶段。为了解决这个难题,让代码拥有更高的可读性与更高的安全性,java中提出了泛型的概念。
泛型的本质是数据类型的参数化,这么说显得比较抽象。其实我们可以这样理解,泛型就是用一个指定的标识符来代表具体的数据类型,将具体的数据类型转换为参数,在创建类、接口或者方法时,我们都用指定的标识符来代替具体的数据类型,这样就免去同一个类、接口或者方法对不同的数据类型进行操作时需要重新编写程序的问题。在使用由泛型创建的方法、接口或者类时,只要我们进行一定程度的说明,系统就会自动判断数据的类型,这样也免去了类型转换时可能出现的错误,提高了代码的安全性。
所以其实上,泛型与我们之前学的带参方法有着异曲同工之妙。在带参方法中,我们用形式参数来代表传入方法的具体数据,但是在方法创建时我们并不知道传入的实际参数是什么,只有在使用这个方法的时候才知道要传入一个什么样的数据。泛型也是这样,我们用一个标识符来代表在类、方法或者接口中可能出现的数据类型,但具体会出现什么样的数据类型只有在使用一个方法、实例化一个对象或者实现一个接口的时候才知道。
除了上面谈到的关于泛型的理解的问题,我们还要注意到,虽然程序员在编写程序的时候用了泛型参数来定义类型,但是在编译的时候泛型会被去掉,编译结束后类型参数会被替换成Object。涉及到类型转换时,任然是普通的强制类型转换。我们把这种类型参数在编译后消失的现象叫做类型擦除。虽然目前通过反编译手段任然能够看到编译前的类型参数,但是着并不代表编译后生成的class字节码文件中存在着泛型中的类型信息。
泛型的定义
从理论上来说,泛型可以用任何一个符合规则的标志符来定义,但是那样会有很大概率造成不小的麻烦,因此一般来说我们会用java中指定的几个标识符来定义泛型。这些标识符分别是:E、T、K、V、N、?。
这些标识符每一个都代表了不同的含义。
E——Element,在容器中使用,代表的是容器中的元素。
T——Type,表示的是普通的java类。
K——Key,表示键,例如Map中的Key键。
V——Value,表示值。
N——Number,表示数值类型。
?——表示不确定的java类型。
不难发现,N代表的是Number类,而在包装类中我们提到过,Number类是六种数值类型对应的包装类的父类,也就是说定义泛型时只能用引用类型,不能使用基本数据类型。
泛型类
泛型类是指在类中使用泛型的情况。在类中使用泛型的方法是在类名的后面添加一个尖括号,然后在尖括号中输入泛型标识符,如果涉及到多个泛型,泛型之间用逗号隔开。随后我们就可以在这个类中用泛型来定义类的属性以及方法。这里我们用一个例子来演示泛型类的定义以及使用,以加深对泛型的理解。
在下面的这个程序代码中,我们定义了一个泛型类,泛型采用的是T,代表普通的java类。这里说一下在这个类中定义的泛型属性。在代码private T flag;中,private关键字表示程序私有,T是泛型,表示的是属性flag的数据类型,这里用代码private String flag;来做对比就能发现泛型T替代了数据类型String 的位置。也就是说我们用泛型替代了数据类型,当然没有进行说明的情况之下,泛型T也可以代表Integer或者其他的数据类型。
在测试代码中,我们对泛型类Generic<T>进行了实例化。要注意的是,这里的对象实例化和普通的对象实例化存在区别。在实例化泛型类的对象时,我们首先指定了泛型参数所代表的数据类型,就像在调用含参方法时输入参数一样。然后才创建相关的对象,具体的格式为:
泛型类名称<泛型所代表的数据类型> 对象名 = new 泛型类名称<>();
此外,可以发现,在使用泛型类时也有不确定泛型所代表的参数类型的情况,这时系统会默认为泛型代表的为Object类型。当然这种写法没有问题,但是不建议使用,因为这和没有定义泛型没有区别。
java">/*** 泛型类*/
public class Generic<T> {private T flag;public T getFlag() {return flag;}public void setFlag(T flag) {this.flag = flag;}
}
java">public class TestGeneric {public static void main(String[] args) {Generic<String> flag1 = new Generic<>();flag1.setFlag("agg");System.out.println(flag1.getFlag());Generic flag2 = new Generic();flag2.setFlag(220);System.out.println(flag2.getFlag());Generic<Integer> flag3 = new Generic<>();flag3.setFlag(256);System.out.println(flag3.getFlag());}
}