如何自定义泛型结构:泛型类,泛型接口,泛型方法
泛型类和泛型接口的区别主要是类和接口的区别,所以就不去举泛型接口的例子
自定义泛型类:
以前我们定义的类的元素是非常确定的,但我们可能会涉及到这个类有一个属性,这个属性类型不太确定,可以给类加上泛型,泛型用大写字符表示:通常用T,E,K,V(K,V更多的运用在键值对的泛型中)
类的内部结构就可以使用类的泛型,下面的程序中把T称为类的泛型
接口中不能有构造器
//在实例化的时候指明T到底是什么类型
public class Order<T>{
String orderName;
int orderId;
T orderT;
public Order(){
}
public Order(String orderName,int orderId,T orderT){
this.orderName=orderName;
this.orderId=orderId;
this.orderT=orderT;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public T getOrderT() {
return orderT;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
集合当中在List声明了泛型,ArrayList实现了它,这个泛型也保留了
Order是一个自定义的泛型类,在实际开发中如果需要提供Order的子类SubOrder,继承于Order
如果像这样进行了指明,造SubOrder的对象的时候,就不用加尖括号了(不用写成SubOrder<>了)
class SubOrder extends Order<Integer>{
}
可以直接这么写
由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型
这里注意Order是泛型类,subOrder不是泛型类,就是一个普通的类
class subOrder extends Order<Integer>{
}
public class SubOrder {
public static void main(String[] args) {
subOrder sub=new subOrder();
sub.setOrderT(1233);//调用父类的方法,因为指明了Integer,所以此时要求传入的是Integer类型
}
}
还有一种情况,继承Order但不去指明,此时SubOrder1是泛型类,实例化的时候可以指明类型
public class test01 {
public static void main(String[] args) {
SubOrder1<String> sub2=new SubOrder1<>();
sub2.setOrderT("order2");//此时就要传入String类型
}
}
class SubOrder1<T> extends Order<T>{
}
注意点:
-
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如: <E1,E2,E3>,有几个就放几个
-
泛型类的空参构造器如下:public GenericClass(){}。
而下面是错误的:public GenericClass< E>(){},实例化的时候需要补上,此时new之后的尖括号里面可以为空,new SubOrder1<>();写成这样也可以:new SubOrder1< String>(); -
实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
-
泛型不同的引用不能相互赋值。 尽管在编译时ArrayList< String>和ArrayList< Integer>是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中
-
泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。这里只是认为是Object但不等于是Object,因为在继承方面还是有区别。经验:泛型要使用就一直都用。要不用,就一直都不要用。
-
如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
-
jdk1.7,泛型的简化操作:ArrayList< String> list = new ArrayList<>();
-
泛型的指定中不能使用基本数据类型,可以使用包装类替换。
-
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型
public class Order<T>{
String orderName;
int orderId;
T orderT;
public static void show(){
System.out.println(orderT);//会报错
}
}
这么写也报错
public static void show(T orderT){
System.out.println(orderT);
}
因为类的泛型是造对象的时候指定的,静态结构的加载早于对象的创建,所以是错误的
10. 异常类不能是泛型的,异常类不能声明为泛型类
不能这么写,编译不通过
try{
}catch(T t){
}
public class MyException extends Exception{
//不报错
}
public class MyException<T> extends Exception{
//报错
}
- 不能使用new E[]。否则编译不通过,但是可以:E[] elements = (E[])new Object[capacity];
public Order(){
T[] arr=new T[10];
}//编译不通过
可以这么写
public Order(){
T[] arr=(T[])new Object[10];
}
new的其实还是Object类型的数组,但具体放数据的时候必须放T和其子类的对象,不要放Object对象,否则出类型转换异常,这样才能保证实际执行的时候不会出问题
12. 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
13. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{ }
}
// 2)具体类型
class Son2 extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留,Integer是确定的,另一个是不确定的
class Son4<T2> extends Father<Integer, T2> {
}
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{ }
//这里是子类自己提供了两个泛型参数A,B,是额外的
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
//如果父类中的方法用到了泛型,就是Integer和String,这里是自己额外又定义了两个泛型参数,在自己的东西中还可以用自己的A和B
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
//在保留T1,T2的基础之上,又额外定义了两个
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
//保留T2的同时自己又额外定义了两个
}