大家好,我是一个泛型,我的出现是为了代表不同的参数类型,以减少因为数据类型而对代码造成的束缚,同时使代码更简洁。
比如小明为公司开发一个接口,这个接口可以传入不同的数据类型,那么在我没诞生前,小明需要写多个接口,如下:
public interface ICommonGeneric {
String get(String a);
Integer get(Integer a);
}
实现类需要实现每一个接口,尽管处理逻辑相同:
public class CommonGenericImpl implements ICommonGeneric {
@Override
public String get(String a) {
//do something
return a;
}
@Override
public Integer get(Integer a) {
//do something
return a;
}
}
那么有没有一种方式可以代表不同参数的类型呢,也就是说不用在接口中具体指定到底是String还是Integer,而是在实现时确定呢?于是我便出现了。
大家可以叫我泛型化参数,因为我也代表一个参数,只是我代表的是参数的类型,我也有形参和实参。
形参 一般通过一对尖括号 加 T(一般用一个大写字母表示)等,也可以包括多个形参,实参就是调用者传给我的,如果是接口泛型,需要在类上指明泛型类型,如果是泛型方法的话,通过编译器的类型参数推断得到,不用码农们指定啦。下面介绍下简单泛型,接口泛型和方法泛型的使用:
简单泛型: 在类上通过 声明泛型,可以看到通过引用泛型提高了类的可重用性,使得方法可以接受不同类型的参数, 在实例化类时指定泛型的实参
public class SimpleGeneric<T> {
void doSomething(T t) {
//do something
}
T getT( T t ) {
doSomething (t);
return t;
}
public static void main(String[] args) {
SimpleGeneric<String> stringSimpleGeneric = new SimpleGeneric<> ();
stringSimpleGeneric.doSomething ("nothing");
String result = stringSimpleGeneric.getT ("nothing");
}
}
可以看到,使用泛型类似于一个工厂模式,只是工厂的条件变成了泛型的实参而已。
接口泛型:如上面小明的代码,可以通过添加一个接口泛型来代表传入的参数类型,而在具体的实现时,在实现类指定泛型的实参
public interface IImprove<T> {
T get(T t);
}
实现类,指定泛型实参:
public class ImproveImpl implements IImprove<String> {
@Override
public String get(String s) {
//do something
return s;
}
}
接下来看一下方法泛型:
public class GenericityTest1 {
public static <T> T saySomething(T t){
//do something
System.out.println (" She say " + t);
return t;
}
public static void main(String[] args) {
String something = GenericityTest1.saySomething (" good morning ");
}
}
泛型方法一般通过在方法前加一个 来声明一个泛型,方法泛型的实参通过 编译器 对具体调用的参数的类型获得,比如在main函数中调用saySomething时,编译器推断出这个参数为String类型,因此确定泛型T类型为String
问题来了,如果码农们想指定泛型所代表的类型范围时,该怎么办呢?java为泛型重用了extends关键字。
public static <T extends Integer> T saySamethingUserful(T t) {
//do something
System.out.println (" She say " + t);
return t;
}
public static void main(String[] args) {
GenericityTest1.saySamethingUserful (1);
}
比如上面的泛型方法,在调用时,就只能使用Integer或其子类类型的泛型,感觉一下子回到了解放前,因为我们知道Integer是final的,实际上这个例子限制了只可以使用Integer类型的参数。
这里一个典型的应用就是我们需要传入的参数是某个接口的实现类时:
比如一个接口
public interface IGenericInterface {
void sayGeneric();
}
在方法中限定参数必须为这个接口的实现类,那么在调用时传入这个接口的实现类,以解耦代码
public static <T extends IGenericInterface> T genericFunc(T t){
return t;
}
public static void main(String[] args) {
GenericTestImpl.genericFunc (new IGenericInterface () {
@Override
public void sayGeneric() {
//do something
}
});
}
最后需要注意泛型的擦除,泛型只在编译时起效果,在编译后的class文件中是看不到泛型的。因为泛型的开发者为了保证java的向后兼容性而采取的一种做法。所以我们无法使用instanceof 关键字
public static <T extends IGenericInterface> T genericFunc(T t){
t instanceof T; //error
return t;
}