一、泛型基础使用
你有写过这样的类吗?
class StorageString {
String date;
private void print() {
System.out.println(date);
}
}
class StorageInt {
int date;
private void print() {
System.out.println(date);
}
}
StorageString storageStr = new StorageString<>();
storageStr.date = "泛型";
storageStr.print();
StorageInt storageInt = new StorageInt<>();
storageInt.date = 2;
storageInt.print();
或者这样的方法?
public void storageString(String str){
StorageString storageStr = new StorageString();
storageStr.date = msg;
storageStr.print();
}
public void storageInt(int d){
StorageInt storageInt = new StorageInt();
storageInt.date = d;
storageInt.print();
}
因为所需处理对象的类型不同,而不停的重载类或方法,但逻辑相同。泛型就是为了解决这类问题而存在的,我们就使用泛型进行简化如下:
泛型类:
//泛型定义名称(T)随便取,26个字母都可以。
class StorageData<T> {
T date;
private void print() {
System.out.println(date);
}
}
StorageData<String> storageData = new StorageData<>();
storageData.date = "泛型";
storageData.print();
StorageData<Int> storageData = new StorageData<>();
storageData.date = 2;
storageData.print();
泛型方法:
public <T>void storageData(T d){
StorageData<T> storageStr = new StorageData();
storageStr.date = d;
storageStr.print();
}
泛型还可以使用extends关键字给它设置限制,比如:
public <T extends Number>void storageData(T d){
StorageData<T> storageStr = new StorageData();
storageStr.date = d;
storageStr.print();
}
这里只对方法举例,类同样适用,T extends Number表示对泛型T设置了上限,传入的参数对象类型将必须是Number的派生类。
还没有完呢!泛型是否可以同时设置多个呢?不管是在方法上还是类中都是可以的。
public <T,D,K>void storageData(T t,D d,K k){
}
甚至泛型的限制也是可以设置多个。
public <T extends String & Test>void storageData(T d){
StorageData<T> storageStr = new StorageData();
storageStr.date = d;
storageStr.print();
}
public interface Test{
}
不过需要注意,根据java语法,指定多个界限时,只能有一个是类且必须在第一位,其它接口无限制。上面String是类,所以只能有一个且在第一位,Test是接口,像这样的后面指定多少个都没有限制。
二、 泛型通配符
先对接下来的例子类结构做一个说明,有一个Earth(地球)类,地球上有Biology(生物)类,派生的Animal(动物)类,再由Animal派生的Hippo(河马)类。大概如下:
public class Earth<T> implements Star<T> {
@Override
public void setBiology(T t) {
}
@Override
public T getBiology() {
return null;
}
}
public class Biology {
}
public class Animal extends Biology{
}
public class Hippo extends Animal {
}
看两个泛型方法例子:(注意earth1和earth2对象的泛型)
两种泛型方法,被分别调用时都没有编译异常,但如果是下面这种泛型方法,就会存在编译器报错。
嗯?我们看下报错内容。
原因:没有类型变量的实例存在,使Earth<Animal>符合Earth<Biology>。
按正常的理解,Earth<Animal>肯定是能够符合Earth<Biology>参数限制的,因为Animal是Biology的派生类,那为什么这里编译器报错呢?我猜测是当一个类作为另一个类的泛型,且另一个类又作为一个泛型的限制类;简单的理解,当一个类不是直接作为泛型的限制类时,这个类的派生关系将无法体现出来。所以这里编译器识别不了。带着这个猜想,我们再看一个例子。
报同样的错,Biology类也不是直接作为参数类型,那对这个猜想是不是可以有一个更准确的定义,当一个类不是直接作为参数对象或泛型限制类,而是间接作为参数对象或泛型限制类的泛型,处在第二层指定泛型;此时此类的派生关系将无法被编译器识别。至于编译器为什么要这么设定,需要深入研究!那有解决办法没有呢?这里就是通配符出场的时候了。
这种形式也是可以的,但是当我们需要对Earth对象的操作跟泛型相关时,就无法进行了。
引出通配符的两种写法,?extends Class / ? super Class 。
我们先看第一种形式extends(上界通配符),注意earth1和earth2对象的泛型。
调用不报错,可是对Earth对象set值又报错了,这里原因也是因为编译器原因,这里没有得知准确的官方解释,只需要记住上界通配符用来安全的取值。
第二种形式super(下界通配符),注意earth1和earth2对象的泛型。
同样调用不报错,对Earth对象set值也不报错,但是仔细观察会发现get值只能接受Object对象,这里就需要记住下界通配符用来安全的赋值。
上界通配符用来安全的取值:知道最大安全取。
下界通配符用来安全的赋值:知道最小安全赋。
最后,我们同时编写两种通配符的方法。
需要注意,不管是通配符定义的上界/下界的差异,还是界限类的不同都不能触发方法重载的规则。