【相关文章】
1.泛型的概念
Java5才有了泛型,在泛型被加入之前。Java中的对象集合,往往是需要转换为Object对象,取出对象也是要再次进行强制类型转换,这样的操作带来了ClassCastException风险。
泛型的加入,使得集合在编译阶段就可以推断出集合中元素类型,减少了代码臃肿和异常风险。
java5 允许程序在创建集合时指定集合元素的类型,如List指定类型为String,它只可以保存String对象,这被称为“参数化类型(parameterized type)”,又称“泛型(Generic)。”
//java7 的菱形语法("<>"标识)可以省略构造器中泛型对象类型
List<String> names = new ArrayList<>();
names.add("小王1");
names.add("小王2");
names.add("小王3");
2.泛型的3种常见使用–泛型类、泛型接口和泛型方法
定义类、接口或方法时使用类型形参(泛型),在声明变量、创建对象、调用方法时候会动态指定这个泛型。
2.1 泛型类
public class Apple<T> {
//使用T类型定义实例变量
private T info;
public Apple(){}
//使用T类型来定义构造器
public Apple(T info){
this.info = info;
}
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
}
//使用
Apple<String> name = new Apple<>("苹果");
Apple<Double> price = new Apple<>(5.0);
泛型类与普通类的区别就是在方法名后使用<T>进行了声明泛型形参,接下来可以在类中将T作为类型使用(如上面直接定义变量info)。
2.2 泛型接口
//泛型形参E
public interface loadResult<E>{
//E 作为参数类型
void returnResult(E result);
}
public interface addResult<E>{
//E 作为类型使用
E add();
}
泛型接口跟泛型类声明基本一样,使用的范围也是相同。
2.3 泛型方法
List<String>不是List<Object>子类,如果想要将每一个String类型的数组元素放到一个集合中,集合的数据类型只能是String:
String[] fruitArray = new String[]{"apple","banana","peach"};
List<String> fruitList = new ArrayList<>();
array2Collection(fruitArray,fruitList);
Log.d(TAG,">>>>>> fruitList = " + fruitList);
//代码省略...
private void array2Collection(String[] array, List<Object> list) {
for (String str : array) {
list.add(str);
}
}
上面的代码在编译环境下会报如下异常信息:
如果后期需要放置Integer类型,就需要再次新建Integer的集合,这样的方法功能十分有限。为了解决这个问题,Java设计了泛型方法。泛型方法对比普通方法多了泛型形参声明,所有的泛型形参声明放在修饰符和方法返回值中间。
修饰符 <T,S> 返回值类型 方法名(形参列表){
//方法体
}
private static <T> void add(T t) {
Log.d(TAG, ">>>>>>" + t + "-100");
}
使用泛型方法来完成将数组数据元素放到集合中
//定义一个泛型方法,T泛型形参仅可以在该方法中使用
private <T> void array2Collection(T[] array, List<T> list) {
for (T str : array) {
list.add(str);
}
}
//泛型方法使用
String[] fruitArray = new String[]{"apple", "banana", "peach"};
List<String> fruitList = new ArrayList<>();
Integer[] integers = new Integer[]{0, 1, 2, 3};
Float[] floats = new Float[]{1.0f, 2.0f, 3.2f};
List<Number> numberList = new ArrayList<>();
array2Collection(integers, numberList);
Log.d(TAG, ">>>>>> Integer[] numberList = " + numberList);
array2Collection(floats, numberList);
Log.d(TAG, ">>>>>> Float[] numberList = " + numberList);
array2Collection(fruitArray, fruitList);
Log.d(TAG, ">>>>>> List<String> fruitList = " + fruitList);
//输出结果:
D: >>>>>> Integer[] numberList = [0, 1, 2, 3]
D: >>>>>> Float[] numberList = [0, 1, 2, 3, 1.0, 2.0, 3.2]
D: >>>>>> List<String> fruitList = [apple, banana, peach]
3.类型通配符
接下来了解下类型通配符的规则。Java集合不支持型变,即List<String>并不是List<Object>的子类。为了表示泛型父类,可以考虑使用类型通配符作为参数。
类型通配符使用符号"?"表示。常见如集合List<?>、Map<?>等场景。
//定义方法
private void test2(List<?> c) {
for (int i = 0; i < c.size(); i++) {
Log.d(TAG, ">>>>>>>> result = " + c.get(i));
}
}
//使用类型通配符
List<String> list = new ArrayList<>();
list.add("String_0");
list.add("String_1");
test2(list);
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
test2(list1);
//输出结果:
D: >>>>>>>> result = String_0
D: >>>>>>>> result = String_1
D: >>>>>>>> result = 1
D: >>>>>>>> result = 2
使用类型通配符的List代表它是各种泛型List的父类,不能向List<?>中添加元素。集合代表的是一组对象,不允许把对象放进未知类型的集合中。
3.1 类型通配符的上/下限
List<?>表示的是所有父类,为了限制其范围Java提供了被限制的泛型通配符。其表示如下:
//代表集合中的元素都是Fruit子类
List<? extends Fruit>
既然有上限自然有下限,通配符下限表示方式为:
List<? super Apple>
4.泛型形参的上限
有了对通配符上限的了解,泛型形参上限的概念也很好理解了。如下代码示例:定义类Orange,限制传递的泛型必须是Number子类或本身。
/**
* 泛型形参上限
* @param <T>
*/
public class Orange<T extends Number>{
T temp;
public Orange() {
}
}
//使用Orange类
Orange<Integer> orange = new Orange<>();
Orange<Double> orange1 = new Orange<>();
Orange<String> orange2 = new Orange<String>();//①编译报错
上面代码示例①处编译报Type parameter xxx is not within its bound。: