接口
接口到底是干什么的?答:接口是用来指明相关或者不相关类的多个对象的共同行为(共同方法)。例如,使用正确的接口,可以指明这些对象是可比较的、可食用的、以及可克隆的等等。
泛型
泛型使我们可以定义带泛型类型的类或接口或方法,随后编译器会用具体的类型来替换它。
优点:能够在编译时而不是在运行时检测出错误,因此可以提高软件的可靠性和可读性。其实很好理解,我们自定义一个整型栈类,栈类里面是一个ArrayList,如果ArrayList里面元素类型使用Object,此时无论是Integer类还是String类的实例都可以放进去,但是应该放进去Integer类实例,如果传错了(譬如传了一个String类实例)将不会报错,但使用了泛型的话,在编译时就会报错。
注意:泛型类型必须是引用类型,不能使用基本数据类型(int,double,char)来替换泛型类型,但可以使用Integer,这时可以传入一个int,Java会将其包装为Integer,这个过程称作自动打包。同样,如果元素是包装类型(Integer,Double,Character),那么可以直接将这个元素赋给一个基本类型的变量,这个过程称为自动拆箱。
定义泛型类:
为了定义一个类为泛型类型,需要将泛型类型放在类名之后,例如StringStack<E>。
注意:为了创建一个字符串堆栈,可以使用new StringStack<String>(),但需要注意定义这个类的构造方法时不写泛型标识,应该被定义为public StringStack() {},即不进行显式定义,使用Java默认提供的无参构造方法即可。
定义泛型方法:
为了定义一个方法为泛型类型,要将泛型类型放在方法返回类型之前,例如<E> void printMax(E o1, E o2)。
可以为静态方法定义泛型类型。
通配泛型:
1.为什么需要通配泛型?
首先,Integer是Number的子类。我们现在定义了一个方法用来找到栈中的最大值,签名是public static double max(GenericStack<Number> stack),如果这时有一个类型为GenericStack<Integer>的栈实例integerStack,语句int a = max(integerStack)会报错,因为integerStack不是GenericStack<Number>的实例。尽管Integer是Number的子类型,但是GenericStack<Integer>并不是GenericStack<Number>的子类型,这时我们需要通配泛型类型。
2.通配泛型类型
通配泛型类型有三种:?(非受限通配符)、? Extends T()或者? super T。
?称为非受限通配,它和? extends Object是一样的。? Extends T()称为受限通配,表示T或T的一个子类型。? super T称为下限通配,表示T或T的一个父类型。
将上面的函数签名改为public static double max(GenericStack<? Extends Number> stack)即可,<? Extends Number>是一个表示Number或Number子类型的通配类型。
消除泛型:
编译器可使用泛型信息,但这些信息在运行时是不可用的,这被称为类型消除。编译器使用泛型类型信息来编译代码,但是随后会消除它,即泛型信息在运行时是不可用的。泛型存在于编译时,一旦编译器确认泛型类型是安全使用的,就会将它转换为原始类型。
例子:
将a编译成b
如果一个泛型类型是受限的,那么编译器就会用该受限类型来替换它。
例子:
将a编译成b
非常需要注意的是,不管实际的具体类型是什么,泛型类是被它的所有实例所共享的。