泛型乃强数据类型语言一特性,旨在规定可变部分,Java中主要指参数。其规定将被编译器即运行环境所参考,在进行编译时进行实时报警。
泛型一曰“参数化类型”,参数分为实参与形参,此处所知参数类型表示传入参数必定的类型。虽形参上已声明类型,但其是为适应所有场景所设置,在单独使用场景需特殊规定,且此类规定之后所传入参数可被原形参声明类型所参考,即多态。
泛型旨在无有新类型情况下限制参数类型,此类方式可应用于类、接口及方法,其泛型类型称为泛型类、泛型接口及泛型方法。若其泛型任意,可在调用其构造或方法时,即可传入实际泛型将其转换。
例如ArrayList集合一例,其虽可传入任意类型,也为通用所设计,但对于专有类型集合存在危险的转型错误,则需控制传入集合之中参数之类型,具体控制为各方法参数类型:
List arrayList = new ArrayList(); arrayList.add("aaaa"); arrayList.add(100); ? for(int i = 0; i< arrayList.size();i++){ String item = (String)arrayList.get(i);//在此处转型将出现问题 } List<String> arrayList = new ArrayList<String>(); ? arrayList.add("aaaa"); arrayList.add(100);//若方法之参数类型非其泛型所规定类型,则出现编译错误
泛型仅应用于编译期,并不会携带入class文件中。
代码中泛型若为大写字母类型,皆为任意类型,之所以会出现各类大写字母,仅因其语义化:
T:Type,即类型一意。
K:key,意为控制键位类型。
V:value,意为控制值位类型。
E:element,意为元素类型。
问号亦为一种泛型类类型,实意无效而,其与未加类型并无二样。其无法在使用过程中控制返回或参数类型。具体泛型如何控制参数或返回类型,将在下文中详细介绍。
调用时定义泛型亦可为任意对象类型中其中一个,但万不可使用基本类型,可用其包装类型。
泛型可在调用时缺省定义,即为Object类型,即任意类型。
类上定义泛型要在其类名后书写泛型定义,方法定义泛型要在方法调用时写入泛型类型定义。返回值泛型类型无需定义,因其已被类定义,在收取返回值时即可收取类中定义类型。
将泛型定于类上,即此类成为泛型类,其中成员皆可参考其泛型做参数约束。代码可见其所控制,位于构造中所用泛型亦将影响到get方法之返回值类型,具体其类型定义将于创建时定义:
//在实例化泛型类时,必须指定T的具体类型 public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; ? public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } ? public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } } //传入的实参类型需与泛型的类型参数类型相同,即为Integer. Generic<Integer> genericInteger = new Generic<Integer>(123456); ? //传入的实参类型需与泛型的类型参数类型相同,即为String. Generic<String> genericString = new Generic<String>("key_vlaue");
泛型接口用法与泛型类同,其将影响其子实现类中各参数或返回值类型:
//定义一个泛型接口 public interface Generator<T> { public T next(); }
泛型接口子实现类声明泛型,可为父接口相同类型,若实现时父接口类型无有类型,则子类必定要与其同样声明为无类型:
/** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGenerator<T> implements Generator<T>{ * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */ class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
若在实现接口时书写了其泛型类型,本类即可不书写泛型,但其重写方法之参数或返回值类型必要控制为与父类相同:
/** * 传入泛型实参时: * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T> * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。 */ public class FruitGenerator implements Generator<String> { ? private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; ? @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } }
泛型接口与类,皆在实例化时指定其各项类型,而泛型方法是在调用方法时指明泛型类型。
/** * 泛型方法的基本介绍 * @param tClass 传入的泛型实参 * @return T 返回值为T类型 * 说明: * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。 * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 */ public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; }
在泛型类中使用类泛型的方法并不是泛型方法,只有在方法修饰符与返回值类型之间存在泛型声明的才可称为泛型方法。
静态泛型方法无法访问类中泛型,若静态方法所操作之引用类型飘忽,必要将泛型定于方法上。
public class StaticGenerator<T> { .... .... /** * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 * 如:public static void show(T t){..},此时编译器会提示错误信息: "StaticGenerator cannot be refrenced from static context" */ public static <T> void show(T t){ ? } }
可控制传入类型必定为某类之父或其子。
//声明showKeyValue方法参数:一个Generic对象,但其泛型必定为Number类之子 public void showKeyValue(Generic<? extends Number> obj){} Generic<String> generic1 = new Generic<String>("11111"); Generic<Integer> generic2 = new Generic<Integer>(2222); Generic<Float> generic3 = new Generic<Float>(2.4f); Generic<Double> generic4 = new Generic<Double>(2.56); ? //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类 //showKeyValue(generic1); ? showKeyValue(generic2); showKeyValue(generic3); showKeyValue(generic4);
深耕静禅_Java_枚举_2020年4月4日14:22:55
原文:https://www.cnblogs.com/agoodjavaboy/p/12634242.html