Java中关于泛型接口的使用说明
作者:无奈的码农
什么是泛型接口
泛型接口是在接口类中有一个未知对象,一个可变的对象。
从Java 5开始,引入了参数化类型(Parameterized Type)的概念,改造了所有的Java集合,使之都实现泛型,允许程序在创建集合时就可以指定集合元素的类型,比如List就表名这是一个只能存放String类型的List;
泛型(Generic):就是指参数化类型,上面的List就是参数化类型,因此就是泛型,而String就是该List泛型的类型参数;
泛型的好处
- 使集合可以记住元素类型,即取出元素的时候无需进行强制类型转化了,可以直接用原类型的引用接收;
- 一旦指定了性参数那么集合中元素的类型就确定了,不能添加其他类型的元素,否则会直接编译保存,这就可以避免了“不小心放入其他类型元素”的可能;
- 上述保证了如果在编译时没有发出警告,则在运行时就一定不会产生类型转化异常(ClassCastException);
- 显然,泛型使编程更加通用,并且代码也更加简洁,代码更加容易维护;
定义泛型类、接口
不仅Java的集合都定义成了泛型,用户自己也可以定义任意泛型的类、接口,只要在定义它们时用<>来指定类型参数即可;
例如:
public interface SqlInterFace<T> { ... }
其中指定了该泛型的类型参数,这个T是一个类型参数名,用户可以任意命名(就像方法参数的形参名一样),只有在定义该泛型的对象时将T替换成指定的具体类型从而产生一个实例化的泛型对象,一个泛型接口可以被多个类实现,可以减少接口的开发。
例如:
SqlInterFace<BondCode> interFace = new BondCodeService(); SqlInterFace<BondDeal> dealSqlInterFace = new BondDealService(); SqlInterFace<BondDealRecord> bondCodeService = new BondDealRecordService();
实现/继承泛型接口/泛型类
定义泛型和使用泛型的概念:主要区别就是定义和使用
- 那Java的方法做类比,Java的方法在定义的时候使用的都是形参(虚拟参数),但是在调用方法(使用方法)的时候必须传入实参;
- 同样泛型也有这个特点,泛型的类型参数和方法的参数一样,也是一种参数,只不过是一种特殊的参数,用来表示未知的类型罢了;
- 因此,泛型也是在定义的时候必须使用形参(虚拟参数,用户自己随意命名),但是在使用泛型的时候(比如定义泛型引用、继承泛型)就必须使用实参,而泛型的实参就是具体的类型,像String、Integer等具体的类型(当然也可以是自定义类型);
- 泛型定义的时候使用形参,例如:public class MyGeneric { … } // T就是一个自己随意命名的类型形参
- 使用泛型的时候必须传入实参:
- 定义引用(对象)的时候毫无疑问,肯定需要传实参:ArrayList list = …; // 必须用具体的类型,像这里就是String来代替形参,即实参
实现/继承一个泛型接口/类的时候
- 你在实现/继承一个接口/类的时候实际上是在使用该接口/类,比如:public class Son extends Father { … }中Father这个类就是正在被使用,毫无疑问,必定是在使用;
- 因此泛型其实无法继承/实现,因为在实现/继承的时候必须为泛型传入类型实参,给定实参后它就是一个具体的类型了,就不再是泛型了
示例:
public class MyType extends MyGeneric { … } // implements、extends的时候必须传入类型实参,因为实在使用泛型!!
原则上,任何编程语言都不允许泛型模板层层继承!!
继承之后,父类/接口中的所有方法中的类型参数都将变成具体的类型,你在子类中覆盖这些方法的时候一定要用具体的类型,不能继续使用泛型的类型形参了
泛型参数继承
- 上面派生出来的类不是泛型,是一个实体类型,因为其继承的泛型是具有类型实参的,而Java还支持一种特殊的语法,可以让你从泛型继续派生出泛型,而泛型的类型参数可以继续传承下去;
- 语法如下:
class Father<T> { ... }class Son<T> extends Father<T> { ... }
- 即子泛型可以传承父泛型的泛型参数,那么在子类中泛型参数T就和父类的完全相同,还是照常使用(和父类一样正常使用);
注意:
这里extends Father了,因此父类泛型Father就是被使用了,而按照之前讲的规则,使用给一个泛型是必须要指定类型实参的!
因此这里的这个语法是一种特殊语法,Java专门为这种语法开了后门,这种语法只有在类型参数传承的时候才会用到(即上面这种应用);
一旦使用了这种语法,就表示要进行类型参数的传承了(即父类的T传递给子类继续使用,因此子类也是一个跟父类一样的泛型);
并且一旦使用了这种语法,那么子类定义中的Son和extends Father中的类型参数必须和定义父类时的类型参数名完全一样!!
以下三种情况全部错误(全部发生编译报错):
class Father<T> { } class Son<E> extends Father<T> { } class Father<T> { } class Son<T> extends Father<E> { } class Father<T> { } class Son<E> extends Father<E> { }
必须全部使用和父类定义相同的类型参数名(T)!
才行,这是Java语法的特殊规定;
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。