黑马程序员-JAVA基础-Java 集合之泛型
所谓泛型就是允许在定义类、接口时指定类型形参,这个类型形参将在声明变量、创建对象时确定。增加了泛型支持后的集合,完全可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型。即解决一些安全问题;同时还可以让代码变得更加简洁。
一.使用泛型
泛型的格式:通过<>来定义要操作的引用数据类型。
1 public class GenericDemo { 2 public static void main(String[] args) 3 { 4 // 创建一个只能保存字符串的List 集合 5 List<String> strList = new ArrayList<String>() ; 6 strList.add("Generic") ; 7 // 如果存放其他对象这回出现编译错误。 8 System.out.println(strList); 9 } 10 }
使用泛型的好处:
1、将运行时期出现的ClassCastExcpetion , 转移到了编译时期。方便于程序员解决问题,让运行时期问题减少。
2、避免了强制转换的麻烦。
如下代码:
1 class StringDemo 2 { 3 String name ; 4 public StringDemo(String name ) 5 { 6 this.name = name ; 7 } 8 } 9 public class GenericDemo { 10 public static void main(String[] args) 11 { 12 List list = new ArrayList() ; 13 list.add(new StringDemo("烦烦烦烦烦01")) ; 14 list.add(new StringDemo("烦烦烦烦烦02")) ; 15 list.add(new StringDemo("烦烦烦烦烦03")) ; 16 list.add(new StringDemo("烦烦烦烦烦04")) ; 17 18 list.add(1000) ; 19 MyIterator(list) ; 20 } 21 // 定义遍历方法: 22 public static void MyIterator(List list) 23 { 24 Iterator it = list.iterator() ; 25 while (it.hasNext() ) 26 { 27 StringDemo s = (StringDemo) it.next() ; 28 System.out.println(s.name); 29 } 30 } 31 }
}
在调用MyIterator(List list) 方法时会发生ClassCastException 异常。而且在编译时是不会有任何提示,只有运行时会出现,所以使的程序存在安全隐患。
如果使用泛型则会在编译时提示错误,而且在遍历时不需要强制转换。如:
1 class StringDemo 2 { 3 String name ; 4 public StringDemo(String name ) 5 { 6 this.name = name ; 7 } 8 } 9 public class GenericDemo { 10 public static void main(String[] args) 11 { 12 List<StringDemo> list = new ArrayList<StringDemo>() ; 13 list.add(new StringDemo("烦烦烦烦烦01")) ; 14 list.add(new StringDemo("烦烦烦烦烦02")) ; 15 list.add(new StringDemo("烦烦烦烦烦03")) ; 16 list.add(new StringDemo("烦烦烦烦烦04")) ; 17 18 // 下面一行代码在编译时会出错: 19 list.add(1000) ; 20 MyIterator(list) ; 21 } 22 // 定义遍历方法: 23 public static void MyIterator(List list) 24 { 25 Iterator<StringDemo> it = list.iterator() ; 26 while (it.hasNext() ) 27 { 28 System.out.println( it.next().name); 29 } 30 } 31 }
注意:在使用Java提供的对象时,什么时候写泛型呢?
只要见到<> (<>就是用来接收类型的。),就要定义泛型。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
二.了解泛型
ArrayList<E> 类定义和ArrayList<Integer> 类引用中涉及的术语:
> 整个称为ArrayList<E> 泛型类型。
> ArrayList<E> 中的E称为类型变量或类型参数。
> 整个ArrayList<Integer> 称为参数化的类型。
> ArrayList<Integer> 中的Integer 称为类型参数的实例或实际类型参数。
> ArrayList<Integer> 中的<> 念着typeof
> ArrayList 称为原始类型
参数化类型不考虑类型参数的继承:
> Vector<String> v = new Vector<Object>() ; //错误
> Vector<Object> v = new Vector<String>() ; //也错误
创建数组实例时,数组的元素不能使用参数化的类型:
> Vector<Integer> vectorList[] = new Vector<Integer>[10] ; //错误
三.定义泛型类
除了Java提供了一些类增加了泛型支持外,我们可以定义泛型支持的类。那么在什么时候定义泛型类呢?
当类中操作的引用数据类型不确定时可以定义泛型类。
格式如下:
class Tools<T> { }
具体操作:
1 //定义一个工具类Tools 2 //因为不知道要操作的类型是什么所增加泛型支持 3 class Tools<T> 4 { 5 // 包含输出函数: 6 public void sop(T t) 7 { 8 System.out.println("sop:"+t); 9 } 10 } 11 //定义一个Books 类 12 class Books 13 { 14 private String name ; 15 public Books(String name) 16 { 17 this.name = name ; 18 } 19 // 重写toString 方法 20 public String toString() 21 { 22 return "name = " + name ; 23 } 24 } 25 public class GenericText 26 { 27 public static void main(String[] args) 28 { 29 // 创建一个Tools 实例tool ,定义 tool 要操作的数据类型为Books 30 Tools<Books> tool = new Tools<Books>() ; 31 // tool 可以操作 Books 类型,还可以操作Integer 类型和String类型。 32 tool.sop(new Books("诛仙")); 33 } 34 }
定义一个Tools 类 用来完成打印操作,但是应为不知道要操作的数据类型是什么,所以可以定义成泛型类。
三.泛型方法
泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定,
那么可以将泛型定义在方法上。
定义泛型方法格式如下:
public <T> void show(T t) 注意:<>放在修饰符后面,返回值前面 { }
具体操作如下:
1 //定义一个工具类Tools 2 //因为不知道要操作的类型是什么所增加泛型支持 3 class Tools<T> 4 { 5 // 包含输出函数: 6 public void sop(T t) 7 { 8 System.out.println("sop:"+t); 9 } 10 // 定义的泛型方法: 11 public <T> void show (T t) 12 { 13 System.out.println("show:"+t); 14 } 15 } 16 //定义一个Books 类 17 class Books 18 { 19 private String name ; 20 public Books(String name) 21 { 22 this.name = name ; 23 } 24 // 重写toString 方法 25 public String toString() 26 { 27 return "name = " + name ; 28 } 29 } 30 public class GenericText 31 { 32 public static void main(String[] args) 33 { 34 // 创建一个Tools 实例tool ,定义 tool 要操作的数据类型为Books 35 Tools<Books> tool = new Tools<Books>() ; 36 // tool 可以操作 Books 类型,还可以操作Integer 类型和String类型。 37 tool.sop(new Books("诛仙")); 38 tool.show(new Books("诛仙")) ; 39 // 下面的方法编译时会报错》、: 40 tool.sop(1000) ; 41 tool.sop("String") ; 42 43 // 但下面却不会报错,并且正常运行。 44 tool.show(1000) ; 45 tool.show("String") ; 46 } 47 }
通过上面的代码,可以知道泛型类和泛型方法可以同时定义,且不冲突。但是也有特殊情况:静态方法不可以访问定义类上的泛型,如:
class Tools<T> { public static void method(T t) { } } 上面的书写是错误的,
如果静态方法操作的引用数据类型不确定,可将泛型定义在方法上:
class Tools<T> { public static <T> void method(T t) { } }
四.泛型限定和通配符
4.1 通配符
类型通配符是一个问号(?):问号作为类型实参传给List 集合写作:List<?>。
1 //定义一个Books 类 2 class Books 3 { 4 private String name ; 5 public Books(String name) 6 { 7 this.name = name ; 8 } 9 // 重写toString 方法 10 public String toString() 11 { 12 return "name = " + name ; 13 } 14 } 15 public class GenericText 16 { 17 public static void main(String[] args) 18 { 19 // 创建一个只能存储 Books 类型元素的 List 集合。 20 List<Books> bookList = new ArrayList<Books>() ; 21 bookList.add(new Books("诛仙")) ; 22 bookList.add(new Books("笑傲江湖")) ; 23 24 // 创建一个只能存储String 类型元素的List 集合 25 List<String> strList = new ArrayList<String>() ; 26 strList.add("Generic001") ; 27 strList.add("Generic002") ; 28 29 MyIterator(strList) ; 30 MyIterator(bookList) ; 31 32 } 33 // 定义个遍历List 集合的方法。 34 public static void MyIterator(List<?> strList) 35 { 36 Iterator<?> it = strList.iterator() ; 37 while(it.hasNext()) 38 { 39 System.out.println(it.next()); 40 } 41 } 42 }
在 MyIterator 方法中使用了类型通配符 ? ,好处是只写一个 遍历方法便可操作List 集合的遍历,缺点是不能调用元素中的特定方法。
4.2 泛型限定:
1、 ? extends E : 可以接收E类型或者E的子类型。上限定。
2、? super E : 可以接收E类型或者E的父类型。下限定。
1 class Books 2 { 3 String name ; 4 public Books(String name) 5 { 6 this.name = name ; 7 } 8 public String toString() 9 { 10 return "name:" + name ; 11 } 12 } 13 class ComicBooks extends Books 14 { 15 public ComicBooks(String name) { 16 super(name); 17 } 18 } 19 class Person_1 20 { 21 String name ; 22 public Person_1 (String name) 23 { 24 this.name = name ; 25 } 26 } 27 public class GenericTreeSet { 28 29 public static void main(String[] args) 30 { 31 // 定义 TreeSet 集合 ,并且里面只存储ComicBooks类型元素,切按照自已的比较规则排序 32 TreeSet<ComicBooks> bookTree = new TreeSet<ComicBooks>(new MyComparable()) ; 33 34 bookTree.add(new ComicBooks("aaaaaa")) ; 35 bookTree.add(new ComicBooks("aaa3gfaaa")) ; 36 bookTree.add(new ComicBooks("afdfef")) ; 37 bookTree.add(new ComicBooks("asdffefq")) ; 38 // 调用 39 MyIterator(bookTree) ; 40 41 // 下面代码编译会出现异常 42 TreeSet<Person_1> p = new TreeSet<Person_1>(new MyComparable()) ; 43 } 44 // 定义遍历方法:只能操作Books 类型或者 Books的子类型 45 public static void MyIterator(TreeSet<? extends Books> bookTree) 46 { 47 Iterator<? extends Books> it = bookTree.iterator() ; 48 while(it.hasNext()) 49 { 50 System.out.println(it.next().toString()); 51 } 52 } 53 } 54 //定义比较器 :按倒序排序且该比较器只适用于 Books 类型或者 Books的子类型 55 class MyComparable implements Comparator<Books> 56 { 57 public int compare(Books o1 , Books o2) 58 { 59 return o2.name.compareTo(o1.name); 60 } 61 }
所以,当我们定义的某些方法只作用与某些类与其子类时,可以通过泛型限定来实现。