Generics(泛型),咱们常常在Java集合或者框架里面常常看见对泛型的使用场景,那么泛型的做用有哪些呢。java
泛型的本质是为了参数化类型,就是将类型由原来的具体的类型参数化,就像方法中的变量参数,此时类型也定义成参数形式(能够称之为类型形参),而后在使用/调用时传入具体的类型(类型实参)。也就是说在泛型使用过程当中,操做的数据类型被指定为一个参数,这种参数类型能够用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。程序员
泛型类/接口的定义,使用一个类型变量T(其余大写字母均可以,不过经常使用的就是T,E,K,V等等),至关于一个占位符,而且用<>括起来,并放在类/接口名的后面。泛型类是容许有多个类型变量的数组
public class GenericsClass<T> {
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
GenericsClass<String> genericsClass = new GenericsClass<>();
genericsClass.print();
}
}
复制代码
public class GenericsClass<T,V> {
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
GenericsClass<String,Integer> genericsClass = new GenericsClass<>();
genericsClass.print();
}
}
复制代码
public interface ImpGenerics<V> {
}
复制代码
而实现泛型接口的类,有两种实现方法安全
public class Generics<T> implements ImpGenerics<T> {
}
复制代码
这中实现的时候,没有指定具体的类型,这种实现方式在建立对象的时候,须要传入具体的类型bash
public class Generics implements ImpGenerics<String> {
}
复制代码
这种是在实现的时候传入具体的实参,建立对象的时候就想普通类同样使用就OK框架
泛型方法,是在调用方法的时候指明泛型的具体类型 ,泛型方法能够在任何地方和任何场景中使用,包括普通类和泛型类,ide
public class GenericsClass<K,V> {
private K date;
private V value;
//普通方法
private V getValue(K date){
return value;
}
//泛型方法
private <T> T genericsMethod(T date){
return date;
}
public GenericsClass(K date, V value) {
this.date = date;
this.value = value;
}
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
GenericsClass<String,Integer> genericsClass = new GenericsClass<>("k",123);
genericsClass.print();
int a = genericsClass.getValue("k");
System.out.println("v="+a);
}
}
复制代码
泛型方法必须经过“<类型占位符>”来声明返回的类型,譬如<V>等函数
一般在使用时候,咱们须要让全部的类型具体同一个方法,咱们须要对类型变量加以约束,好比计算两个变量的最小,最大值,为了确保传入的两个变量必定有compareTo方法?咱们就须要将T限制为实现了接口Comparable的类ui
private <T extends Comparable> T min(T a,T b){
return a.compareTo(b)>0 ? b:a;
}
复制代码
T extends Comparable中 T表示应该绑定类型的子类型,Comparable表示绑定类型,子类型和绑定类型能够是类也能够是接口,同时extends左右都容许有多个,如 T,V extends Comparable & Serializable 注意限定类型中,只容许有一个类,并且若是有类,这个类必须是限定列表的第一个。 这种类的限定既能够用在泛型方法上也能够用在泛型类上this
private <T extends Comparable & Serializable> T min(T a, T b){
return a.compareTo(b)>0 ? b:a;
}
复制代码
public class GenericsClass<K,V> {
private K date;
private V value;
//普通方法
private V getValue(K date){
return value;
}
//泛型方法
private <T> T genericsMethod(T date){
return date;
}
private <T extends Comparable & Serializable> T min(T a, T b){
return a.compareTo(b)>0 ? b:a;
}
public GenericsClass(K date, V value) {
this.date = date;
this.value = value;
}
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
//不能使用基本类型
GenericsClass<String,int> genericsClass = new GenericsClass<>("k",123);
}
}
复制代码
public class GenericsClass<K> {
private K date;
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
GenericsClass<String> genericsClass = new GenericsClass<>();
GenericsClass<String> genericsClass_ = new GenericsClass<>();
System.out.println("泛型类型genericsClass="+genericsClass_.getClass().getName().toString());
System.out.println("泛型类型genericsClass_="+genericsClass_.getClass().getName().toString());
}
}
复制代码
输出
泛型类型genericsClass=com.mtx.javalib.GenericsClass
泛型类型genericsClass_=com.mtx.javalib.GenericsClass
复制代码
不能在静态域或方法中引用类型变量。由于泛型是要在对象建立的时候才知道是什么类型的,而对象建立的代码执行前后顺序是static的部分,而后才是构造函数等等。因此在对象初始化以前static的部分已经执行了,若是你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,由于这个时候类尚未初始化。 4. 不能建立参数化类型的数组
public class GenericsClass<K> {
private K date;
private void print(){
System.out.println("泛型类型");
}
public static void main(String [] args){
//编译会报错
GenericsClass<String>[] genericsClass = new GenericsClass<String>()[3];
}
}
复制代码
为何是这样,主要是Java的泛型实现方式有关,后续会说到
public <T extends Throwable> T testMethod(T t){
try {
System.out.println("泛型类型异常没法捕获");
}catch (T e){
}
return t;
}
复制代码
可是这种方式是能够的
public <T extends Throwable> T testMethod(T t){
try {
System.out.println("泛型类型异常没法捕获");
}catch (Throwable e){
}
return t;
}
复制代码
泛型类能够继承或者扩展其余泛型类,好比List和ArrayList
通常来讲泛型的通配符有两种,
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Bmw extends Car {
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
}
复制代码
public class Bmw extends Car {
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
public static void testMethod(GenericsClass<? extends Car> car){
System.out.println("类型参数只能是Car的子类");
}
public static void main(String[] args){
GenericsClass<Bmw> genericsClass = new GenericsClass();
testMethod(genericsClass);
}
}
复制代码
Java语言中的泛型通常称为伪泛型,它只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型(Raw Type,也称为裸类型)了,而且在相应的地方插入了强制转型代码,所以,对于运行期的Java语言来讲,ArrayList<int>与ArrayList<String>就是同一个类,因此泛型技术其实是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型 因为Java泛型的引入,各类场景(虚拟机解析、反射等)下的方法调用均可能对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等。所以,JCP组织对虚拟机规范作出了相应的修改,引入了诸如Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题,Signature是其中最重要的一项属性,它的做用就是存储一个方法在字节码层面的特征签名[3],这个属性中保存的参数类型并非原生类型,而是包括了参数化类型的信息。修改后的虚拟机规范要求全部能识别49.0以上版本的Class文件的虚拟机都要能正确地识别Signature参数。 另外,从Signature属性的出现咱们还能够得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中仍是保留了泛型信息,这也是咱们能经过反射手段取得参数化类型的根本依据