泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
泛型的定义主要有以下两种:
1.在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
2.在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(人们大多把这称作模板)不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。
一些强类型编程语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的编程语言只能达到部分目的。
上面Tips如果是不了解泛型的还是比较难理解,本章使用代码的形式,可以简洁明了的看出泛型究竟是什么,下面先写一个没有使用到泛型的JAVA代码,如下:
class Person {
private int age;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
Person p = new Person();
p.setAge(3);
System.out.println(p.getAge());
}
}
上面的代码,假设我们 setAge 这个函数要传递的参数不是整型变量了,要传递个 “3 years old” 这个字符串,那么我们需要把 Person 类里面的定义的 int 型变量全都一个一个的改成 String,这显然就很不方便也很浪费时间,那么这时候就可以引入泛型了,泛型就是把类里面的属性类型使用名字来代替,当我们需要把 int 改为 String 的时候,直接修改定义一处即可实现所有类型的替换,还是以代码的形式比较直观,经过修改上面的代码使用泛型后,如下:
/* 这个 T 就是泛型(可以用其它名字,不一定是T),它可以替代类里面使用到的变量类型 */
class Person <T>{
private T age;
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
/* 当我们需要把int改为String,直接往<>里面写入类型即可 */
Person<String> p = new Person<String>();
p.setAge("3 years old");
System.out.println(p.getAge());
/* 这里不能用int,因为泛型是类类型,也需要使用Integer类 */
Person<Integer> p2 = new Person<Integer>();
p2.setAge(3);
System.out.println(p2.getAge());
}
}
当然,上面的 < T > 如果想声明成使用两个泛型的话也是可以的,比如 < T,N > 来代替两个类型,在定义时也需要定义两个类型,比如< String,int >。上面的打印过程如果使用函数来实现,那函数形参的定义能不能也使用泛型呢,上面传递的类他们的泛型都不一样,一个是< String >,一个是< Integer >,那我们在传递给形参的时候可以使用通配符< ? >来定义,也可以把其它已经实例化的对象赋值给通配符的对象,如下代码所示:
class Person <T>{
private T age;
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
Person<String> p = new Person<String>();
p.setAge("3 years old");
//System.out.println(p.getAge());
printInfo(p);
Person<Integer> p2 = new Person<Integer>();
p2.setAge(3);
//System.out.println(p2.getAge());
printInfo(p2);
/* 使用通配符来特定一个对象的时候,只能去获取,无法设置,就是没法setAge */
Person<?> p3;
p3 = p;
p3.getAge();
}
/* <?> 使用这个通配符表示代替传递进来的类型 */
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
}
那么如果想让形参也使用泛型不使用通配符< ? >行不行呢,可以的,看代码的实现:
public class Generics {
public static void main(String args[]) {
Person<String> p = new Person<String>();
p.setAge("3 years old");
//System.out.println(p.getAge());
printInfo(p);
Person<Integer> p2 = new Person<Integer>();
p2.setAge(3);
//System.out.println(p2.getAge());
printInfo(p2);
Person<?> p3;
p3 = p;
p3.getAge();
/* 形参为泛型的使用 */
printInfo2(p);
printInfo2(p2);
printInfo2(p3);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
/* 前面的<T>用来表示形参中的参数是泛型 */
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
}
子类能否也使用父类里面的泛型呢,具体实现看代码:
class Person <T>{
private T age;
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return this.age;
}
}
/* 子类使用泛型 */
class Student<T> extends Person<T> {
}
/* 子类不想使用泛型了,就要指定使用哪一种泛型,如下 */
class Student2 extends Person<String> {
}
public class Generics {
public static void main(String args[]) {
Person<String> p = new Person<String>();
p.setAge("3 years old");
//System.out.println(p.getAge());
printInfo(p);
Person<Integer> p2 = new Person<Integer>();
p2.setAge(3);
//System.out.println(p2.getAge());
printInfo(p2);
Person<?> p3;
p3 = p;
p3.getAge();
/* 形参为泛型的使用 */
printInfo2(p);
printInfo2(p2);
printInfo2(p3);
Student<Integer> s = new Student<Integer>();
s.setAge(10);
printInfo(s);//传入s子类会向上转换为Person父类
/* 子类不使用泛型,只有父类使用的话定义的时候这个子类就不用指定泛型了 */
Student2 s2 = new Student2();
s2.setAge("11 years old");
printInfo(s2);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
/* 前面的<T>用来表示形参中的参数是泛型 */
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
}
当然,接口可以看做是特殊的父类,当然也能使用泛型,如下:
interface Person <T>{
public void setAge(T age);
public T getAge();
}
class Student<T> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge(){
return this.age;
}
}
class Student2 implements Person<String> {
String age;
public void setAge(String age){
this.age = age;
}
public String getAge(){
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
Student<Integer> s = new Student<Integer>();
s.setAge(10);
printInfo(s);//传入s会向上转换了Person
/* 定义的时候这个子类就不用指定泛型了 */
Student2 s2 = new Student2();
s2.setAge("11 years old");
printInfo(s2);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
/* 前面的<T>用来表示形参中的参数是泛型 */
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
}
最后再看看“受限的泛型”,泛型可以定义上限和下限,先来看看泛型的上限,把 T 定义为只能是 Number 类或其子类,用 extemds 表示上限。
interface Person <T>{
public void setAge(T age);
public T getAge();
}
/* 指定只能是 Integer 或 float等 */
/* 这些类都来源于 Number 类 */
class Student<T extends Number> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge(){
return this.age;
}
}
class Student2 implements Person<String> {
String age;
public void setAge(String age){
this.age = age;
}
public String getAge(){
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
/* 加了上限后如果这里的泛型使用String就会出错 */
Student<Integer> s = new Student<Integer>();
s.setAge(10);
printInfo(s);
Student2 s2 = new Student2();
s2.setAge("11 years old");
printInfo(s2);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
}
再看看泛型的下限,把 T 定义为只能是 String 类或其父类,用 super 表示下限,而且super只能用于通配符,如下:
interface Person <T>{
public void setAge(T age);
public T getAge();
}
//不能直接这样用,super只能用于通配符
//class Student<T super String> implements Person<T> {
class Student<T> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge(){
return this.age;
}
}
class Student2 implements Person<String> {
String age;
public void setAge(String age){
this.age = age;
}
public String getAge(){
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
/* 这里因为有下限所以要改成String才能编译通过 */
Student<String> s = new Student<String>();
s.setAge("10 years old");
printInfo(s);
Student2 s2 = new Student2();
s2.setAge("11 years old");
printInfo(s2);
}
/* 只允许那些通配符是String或者String父类的实例化对象 */
public static void printInfo(Person<? super String> p) {
System.out.println(p.getAge());
}
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
}