在Java的开发中可能会遇到在定义一个类的时候,类的类型是不确定的时候。因此像方法一样,定义一个类的形参,类似于方法中的变量参数,将原来定义的类的类型参数化,在调用的时候再传入具体的类型,例如:定义一个泛型类如下:
//定义泛型类
public class Generic<T>{
}
上面定义了一个泛型类Generic,此时只是进行了定义,在使用时才具体确定泛型类Generic的类型。另外Generic泛型类的类型为T,这个T并没有什么实际的含义,只是作为一个一个泛型的类型变量而已。Java中一些常见的泛型的类型变量:
泛型的类型变量 | 意义 |
---|---|
E | 元素(Element),多用于java集合框架 |
K | 关键字(Key),多用于Map集合 |
N | 数字(Number) |
T | 类型(Type) |
V | 值(Value),多用于Map集合 |
接着,我们对Generic泛型类进行扩展:
//定义泛型类
public class Generic<T> {
//定义泛型变量
T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args) {
Generic<String> gen = new Generic<String>();
gen.setData("data为String类型!");
System.out.println(gen.getData());
}
}
泛型的继承
定义一个泛型父类(这里我们定义一个抽象的泛型类):
/**
* 定义一个抽象的泛型类
* @author WL20180723
*
*/
public abstract class Generic1<T> {
T name;
public abstract void test(T t);
}
再定义一个子类继承Generic1泛型类:
/**
* 子类为具体的类
* @author WL20180723
*
*/
public class Child1 extends Generic1<String> {
int name;
public void test(String t) {
this.name= t;
}
}
Child1继承Generic1泛型类,并且在继承后指定了Generic1泛型类的具体类型为String类型,父类中的属性data也随着变成了String类型,子类Child1还有自己定义的name属性。
如果子类也为泛型类,类型在使用时确定(Child2<T> extends Generic1<T>
):
/**
*
* 子类为泛型类,类型在使用时确定
* @author WL20180723
*
*/
public class Child2<T, T1> extends Generic1<T> {
T1 name
public void test(T t) {
}
}
此时的子类中的方法test()
的参数类型随父类中的方法而定,且子类的泛型可以多余父类中的泛型。
若子类为泛型,父类不指定类型:
/**
* 子类为泛型类,父类不指定类型
* @author WL20180723
*
* @param <T>
* @param <T1>
* @param <T2>
*/
public class Child3<T, T1, T2> extends Generic1 {
T1 name;
public void test(Object t) {
}
}
此时,子类中的方法的参数类型,统一使用Object代替。
还有一种,子类不指定类型,父类也不指定类型:
/**
* 子类和父类都不指定类型
* @author WL20180723
*
*/
public class Child4 extends Generic1 {
int name;
public void test(Object t) {
}
}
这种声明方式导致的结果就是子类中的方法的参数类型也统一由Object代替。
泛型接口
首先定义一个父类泛型接口:
/**
* 定义泛型接口
* @author WL20180723
*
* @param <T>
*/
public interface FatherInterface<T> {
public void test(T t);
}
声明一个子类,子类实现父类接口时,父类指定具体类型:
/**
* 声明子类,父类指定具体类型
* @author WL20180723
*
*/
public class ChildInterface1 implements FatherInterface<Integer> {
public void test(Integer t) {
}
}
在声明一个泛型接口的子类时,如果父类指定了类的具体类型(如在本例中为:FatherInterface<Integer>
),子类中的方法中的参数类型跟随父类的具体类型。
如果声明子类,子类和父类都不指定具体类型,如下代码:
/**
* 声明子类,都不指定具体类型
* @author WL20180723
*
*/
public class ChildInterface2 implements FatherInterface {
public void test(Object t) {
}
}
此时,子类类中的方法中的参数类型,统一使用Object代替。
若子类使用泛型,父类擦除(即父类不指定据类类型,也不使用泛型)如下代码:
/**
* 子类使用泛型,父类擦除
* @author WL20180723
*
* @param <T>
*/
public class ChildInterface3<T> implements FatherInterface {
public void test(Object t) {
}
}
此时,和上个例子一样,子类中的方法中的参数类型,统一使用Object代替。
当然,子类泛型个数也可以多于父类泛型个数,具体代码如下所示:
/**
* 子类使用泛型>父类泛型
* @author WL20180723
*
* @param <T>
*/
public class ChildInterface3<T, T1> implements FatherInterface<T> {
public void test(T t) {
}
}
但是父类使用泛型,而子类擦除时,和继承抽象类的泛型类一样,会出现编译错误!!!
在Java泛型中'?'
也有使用,用来代表泛型的类型不确定,在使用时确定类型。注意:'?'
只能在声明一个对象和方法中使用,不能在声明类的时候使用如:
Student<?> s = new Student<String>(); ✔
public static void test(Student<?> s) {} ✔
public class Student<?> {} ✖
关于'?'
还有一些要注意的如下代码(省略了另外定义的两个类People和Animal,People继承了Animal):
/**
* ?类型不确定,使用时确定类型
* ?:在声明类型或者方法时使用,不能在声明类或者使用时使用
* 没有泛型数组
* @author WL20180723
*
* @param <T>
*/
public class Student<T> {
T score;
public static void main(String[] args) {
Student<?> s = new Student<String>();
test(new Student<Integer>());
test2(new Student<People>());
// test3(new Student<People>());//泛型没有多态
Integer[] arr = new Integer[10];
// Student<String>[] arr2 = new Student<String>[10];//不能创建泛型数组
Student<?>[] arr2 = new Student[10];
}
public static void test(Student<?> s) {
}
public static void test2(Student<? extends Animal> s) {
}
public static void test3(Student<Animal> s) {
}
}