Java 泛型解析
在Java中,泛型是通过<>来指定类型的一种方式,在没有泛型时候,我们将元素放入集合中的时候,会将所有的元素转化为Object类型,等到需要用的时候再拿出来将其转化为需要的类型,势必导致了代码的臃肿,例如下面的例子。
不对list进行类型的检查,我们可以将任意的Object类型存入list中,当我们需要使用到的时候需要对其进行转换,相当的麻烦。
List list=new ArrayList();
list.add("123");
list.add("234");
list.add(2);
通过泛型来进行解决数组的类型的问题
List<String> list=new ArrayList();
list.add("123");
list.add("234");
list.add(2);
同样的,当我们在List通过中指定了具体的存入类型过后,我们就可以仅仅存入String,同时不需要进行类型的转换,当我们试图将2添加入list中的时候,将会提示无法将Interger转换为String的问题,确保了代码的严谨和准确。
定义泛型接口,泛型类
我们在编写代码的过程中,可以通过自定义泛型接口,自定义泛型类,实现自定义的泛型。
如下代码所示
public interface TestOut<T>{ //一个泛型的接口
void out(T info);
}
public class Test<T> implements TestOut<T>{ //一个泛型的类
T info;
public Test(T info){
this.info=info;
}
@Override
public void out(T info) {
Log.e("Test",info+"");
}
}
这里简单的实现了一个泛型的类和接口,同时类通过实现接口实现了泛型接口的方法。
值得注意的是,如果上面的实现的接口没有进行泛型的实现,如下面所示
public class Test<T> implements TestOut
实现的out方法将会默认的变成Object方法,不会和T对应起来。
实现泛型的继承
public class TestTwo extends Test{
` public TestTwo(T info) {
super(info);
}
}`
值得注意的是,上面的代码是错误的范例,当我们通过继承的时候,必须对父类的泛型进行指定,不能不对其规定类型,如下的代码就是正确的。
public class TestTwo extends Test<String>{
public TestTwo(String info) {
super(info);
}
}
这里我们为其指定了String的类型,构造函数也可以很自然的找到了对应的泛型类型。
同时,我们也可以对其进行不指定类型,就像是在使用这个类一样,必须对其进行指定类型,如Test或者直接选择不对其进行指定,同样的如果不对其进行指定,将会显示Object类型。
如下所示
public class TestTwo extends Test{
public TestTwo(Object info) {
super(info);
}
}
考虑下面的例子
void Test(List<Object> list){
for (Object o:list){
Log.e("Test",o.toString());
}
}
需要传入的对象的引用是List,理论上说的话,List 类型的引用是可以传入的,因为String是Object的子类,但是当我们进行导入这样编写的时候,发现,编译更本通不过,同时爆出下面的错误
Test(java.util.List<java.lang.Object>) cannot be applied to(java.util.List<java.lang.String>)
也就是说List根本就不是List的父类,所以不能这样的转化。
那如果这个时候我们在不知道需要传入的泛型的类型的时候,怎么传入参数呢?
这个时候就需要用到类型通配符。
类型通配符
类型通配符在java中是一个?来表示的,表示我们不知道当时需要传入的参数,这个时候如上的代码就可以改写如下。
void Test(List<?> list){
for (Object o:list){
Log.e("Test",o.toString());
}
}
这个时候,我们发现当我们进行传入参数的时候就可以进行了,这个时候,不论什么样的参数传入,都会默认的成为Object。
类型通配符的上限和下限
当我们需要指定List<?>中泛型的类型的时候,可以通过List<? extends Object> List<? super Object>
进行指定通配符的上限和下限。
void Test(List<? extends Number> list){ //设定通配符的上限
for (Number o:list){
Log.e("Test",o.toString());
}
}
void Test(List<? super Number> list){
for (Object o:list){
Log.e("Test",o.toString());
}
}
值得注意的是,如果我们尝试像通过通配符实现的集合添加元素的时候,将会编译错误,因为他不知道这个集合的类型,无法对其进行添加元素,如下面的代码。
void Test(List<? extends Number> list){
list.add(2);
}
那如果我们需要像泛型集合中添加元素的时候,应该怎么办?
这个时候就要用到泛型方法。
泛型方法
泛型方法的格修饰符
修饰符<T,S....>
返回值 函数名(形参){
方法体….
}
一个例子如下
public <T>void Test(List<T> list){
}
值得注意的是,如果当我们仅仅只需要传入一个引用的时候,这个时候应该尽量使用类型通配符 ,当我们的传入的引用与方法中的其他引用有着关系的时候,我们尽量使用泛型方法