【Java】Generic 泛型
Generic 泛型
为什么需要泛型?
集合容器再设计阶段/声明阶段不能确定这个容器实际存储的是什么类型的对象
JDK5 以前只能把元素设计为Object基类 在JDK5之后用泛型来约束对象类型
除了元素类型不能确定,但是其他是可以确定的,如何保存,如何管理
把对象的类型设置成一个参数,这个参数的类型就叫做泛型
<E> 元素的类型 看起来就像一个标签对吧
泛型的概念
允许在定义类、接口时通过一个标识表示类中的某个属性的类型或者时某个方法的返回类型或者参数类型
这个类型将在使用时继承或者实现这个接口,用这个类型声明遍历,创建对象时确定实际传入的类型参数,称为类型实参
当我声明了String泛型时,如果再填入其他类型的元素,编译将提示错误,类型不和String匹配
使用了泛型声明String之后,迭代器返回的对象,自动确定了String类型
Foreach遍历可以直接写String类型的变量接受
1 public class CollectionTest { 2 public static void main(String[] args) { 3 List<String> list = new ArrayList<>(); 4 5 list.add("阿伟"); 6 list.add("杰哥"); 7 list.add("阿强"); 8 9 Iterator<String> iterator = list.iterator(); 10 11 while (iterator.hasNext()){ 12 String next = iterator.next(); 13 System.out.println(next); 14 } 15 16 for (String str : list) { 17 System.out.println( str ); 18 } 19 } 20 }
Map的泛型:
public class CollectionTest { public static void main(String[] args) { // 映射接口的泛型 Map<String, Integer> map = new HashMap<>(); map.put("K1",1001); map.put("K2",1002); map.put("K3",1003); map.put("K4",1004); map.put("K5",1005); map.put("K6",1006); // Set的泛型是Map里面的Entry的KV泛型,范中范,泛型嵌套 Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator(); while (iterator.hasNext()){ // 返回Entry类型 Map.Entry<String, Integer> next = iterator.next(); // key类型 和 value类型 String key = next.getKey(); Integer value = next.getValue(); // 打印查看 System.out.println("K = " + key + " : " + " V = " + value); } } }
- JDK5.0的集合容器都修改成带泛型的结构
- 实例化具体集合类的时候可以指名元素的泛型类型
- 集合内部所有定义了使用泛型的方法,属性,都将变成指名的类型
- 泛型的类型必须是引用类型,不能是基本类型,通过包装类实现
- 不使用泛型并不影响使用,类型将默认为Object类型
- JDK7的新特性,实例化的对象不再强制要求写泛型
自定义泛型的使用
public class CollectionTest { public static void main(String[] args) { // 使用泛型实例化 Generic<String,Integer> generic1 = new Generic<>("阿伟",22); // 不使用泛型实例化 ,类型都默认Object Generic generic2 = new Generic("杰哥",28); } } class Generic<T,C>{// 在类名旁声明一个泛型,可以声明多个类型 T name; // 使用时只需要将T 作为一个类型使用 C age; public Generic(T name, C age) { // 作为参数的类型使用 this.name = name; this.age = age; } public T getName() { // 作为方法的返回类型使用 return name; } public C getAge() { return age; } }
- 有泛型就一定要使用泛型初始化
子类继承父类时的泛型
class SubGeneric extends Generic<Integer,String>{ // 在继承父类是声明确定的类型 public SubGeneric(Integer name, String age) { // 下面所调用的泛型全都默认此泛型的指明类型 super(name, age); } @Override public Integer getName() { return super.getName(); } @Override public String getAge() { return super.getAge(); } }
或者不指明类型继续泛型
class SubGeneric<T,C> extends Generic<T,C>{ // 或者不指明类型继续泛型化 public SubGeneric(T name, C age) { super(name, age); } }
- 泛型的个数没有限制,可允许多个泛型声明
- 实例化泛型的类之后必须指明泛型的具体类型
- 编译器可以区分 list<String> list<Integer> 但是实际JVM运行中,只有一个Object类型
- 不指定泛型按Object处理,但不等价于Object? 要么一路使用,要么都不用
- 如果接口和抽象类使用泛型声明,则不可以初始化泛型的实例,还是必须要实现的实现类或继承的子类来泛型实例化
- 泛型可以嵌套泛型的泛型,例如声明A类泛型为T,B类又泛型C,组合关系可以A<B<A<B>>>如此嵌套
关于泛型之后的类型串用
public class CollectionTest { public static void main(String[] args) { List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); // list1 = list2; 编译报错,String并不是Object类 // list2 = list1; 编译报错,String并不是Object类 // 也就是说 List<T1> List<T2> 是并行关系 // 但是A 是 B 的父类, A<G> 是 B<G> 的父类,就可以使用继承关系 } }
我们可以让泛型也实现继承关系!
public class CollectionTest { public static void main(String[] args) { List<Object> list1 = new ArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list1.add(4); list1.add(5); list1.add(6); List<String> list2 = new ArrayList<>(); list2.add("11"); list2.add("12"); list2.add("13"); list2.add("14"); list2.add("15"); list2.add("16"); // list1 = list2; 编译报错,String并不是Object类 // list2 = list1; 编译报错,String并不是Object类 // 也就是说 List<T1> List<T2> 是并行关系 // 但是A 是 B 的父类, A<G> 是 B<G> 的父类,就可以使用继承关系 // 使用通配符泛型建立父子类关系 // 通配符 ? 表示任何泛型类型的公共父类 List<?> list3 = new ArrayList<>(); // 都可以使用 list3 = list1; // Object 是 ? 的子类 printEach(list3); list3 = list2; // String 也是 ? 的子类 printEach(list3); } static void printEach(List<?> list){ Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){ Object o = iterator.next(); System.out.println(o); } } }
使用通配符之后的注意事项
- 该泛型化的集合不能再添加元素,或者说只能添加为null的元素
- 允许读取,和删除,修改也不允许了
通配符的有限制条件的说明
< ? extends T > 只允许泛型的类型是T 和他的子类的引用调用【无穷小, T】
< ? super T > 只允许泛型的类型是T和他的父类的引用调用【T, 无穷大】