首页 > 代码库 > 泛型技术
泛型技术
泛型技术的介绍
这个技术是JDK5中提供的。
针对集合这类容器而言,它中可以存放任意的对象,当任何的对象存放到集合中之后,都被提升成Object类型,当我们从集合中遍历出每个对象的时候,拿到的都是Object类型,这时如果我们想使用对象自身的功能时,就需要向下转型。只要使用向下转型都可能发生ClassCastException异常。
在给集合中存放对象的时候,以Object形式存放进去的。在取出时也是Object类型,当我们在使用对象的特有方法时,进行向下转型,结果发生了异常。造成这个异常的原因是集合中可以存放任意类型的对象,而在取出的时候,我们并没有对取出的类型做任何的判断,直接把取出的类型转成某种特定的类型。在转的时候,不是这种类型的数据,在强转时就会发生异常。
集合是个容器,数组也个是容器。可是数组在定义好之后,它的长度固定了,其中存放的数据类型也被固定了。如果给数组中存放类型不匹配的元素,直接在编译时期就报错。
int[] arr = new int[4];arr[0] = 3.14; //这里直接编译报错。
数组在定义的时候,就把其中存放的数据类型限定死 了。存放的数据一旦不同就直接编译失败。
我们现在就把数组这种思想移到集合上,在定义集合的时候,也限定集合中存放的数据类型,那么在真正给集合中存放数据的时候如果类型不一致,直接让编译失败。这种实现就Java给我们提供的泛型技术。
泛型技术的简单应用
泛型技术:
- 格式:<具体的类型> 这个类型只能是引用数据类型
- 作用(解决的问题):把运行时期的问题,提前到了编译时期。可以保证程序在编译通过之后,运行的时候不会再出现问题。
- 应用:
- 在API中,只要看到接口,类名等后面使用<变量名> 都是泛型技术;
- 当我们在使用这些接口,或者类的时候,要使用者类来指定<> 中间的具体类型。
- 泛型技术在JDK7中升级,升级成菱形技术
public class GenericDemo2 { public static void main(String[] args) { //创建TreeSet集合,并且使用泛型技术 TreeSet<String> set = new TreeSet<String>( new Comparator<String>(){ public int compare(String o1, String o2) { int temp = o1.length() - o2.length(); return temp == 0 ? o1.compareTo(o2) : temp; } } ); set.add("aaa"); set.add("dddd"); set.add("a"); set.add("AAA"); set.add("DDDD"); set.add("aaa"); for (String obj : set) { System.out.println(obj); } }}
泛型类、方法、静态方法泛型
在查阅api的时候发现,api中提供的类上或者接口上应用了泛型技术。oracle公司在设计Java中的这些类的时候,它们在类上,或者接口定义了泛型,那么我们就可以模仿他们,在自己定义类的或者接口的时候,也能够使用泛型技术。
泛型类
需求:定义一个类,负责设置数据,和获取数据
- 设置字符串数据
class Demo{ private String value; public String getValue() { return value; } public void setValue(String value) { this.value =http://www.mamicode.com/ value; }}
- 设置Integer数据
class Demo2{ private Integer value; public Integer getValue() { return value; } public void setValue(Integer value) { this.value =http://www.mamicode.com/ value; }}
上面书写的2个类的确可以设置值也可以获取值,但是它们只能给某个特定的类型来设置值和获取值。当类型不一致时,就无法设置和获取,为了能够给任意对象设置值和获取值,那么:
- 可以把类中的变量类型定义Object类型。
class Demo3{ private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value =http://www.mamicode.com/ value; }}
把类中的变量类型改成Object类型之后的确可以保存任意类型的数据,但是调用获取方法时返回的却变成了Object类型,当要使用对象的特有数据时,又要向下转型。这时我们可以使用泛型技术来完成需求。
这时可以把泛型定义在类上,定以在类上的泛型,就相当于给这个类定义了参数,当使用这个类的时候,要求使用者来指明这个参数到底是什么类型。当把泛型定义在类上时,这个类就成为泛型类。定义在类上的泛型可以在类中直接使用。
- 泛型类
class Demo4<IT>{ private IT value; public IT getValue() { return value; } public void setValue(IT value) { this.value =http://www.mamicode.com/ value; }}
public class GenericDemo { public static void main(String[] args) { //创建拥有泛型的类对象时,要求指明具体的泛型类型 Demo4<String> d4 = new Demo4<String>(); d4.setValue("aaaa"); String value = d4.getValue(); Demo4<Integer> d5 = new Demo4<Integer>(); d5.setValue(123); Integer value2 = d5.getValue(); }}
泛型方法及静态方法泛型
定义在类上的泛型可以在类中的成员变量和方法上使用。有时我们也会遇到方法上要使用的泛型和类上定义的不一致,这时就需要把泛型定义在方法上。
//类上定义的泛型class Test<W>{ private W value; //方法上使用类上的泛型 public void show( W w){ System.out.println(w); } /* * 定义功能,专门负责打印任何数据,可以在这个方法上定义泛型 * 在方法上定义的泛型,必须写在返回值的前面 */ public <Q> void print( Q q ){ System.out.println(q); } /* * 静态方法无法使用类上的泛型。 * 因为类上的泛型的具体类型必须是在创建本类对象的时候明确。 * 而静态的方法它不需要对象就可以直接执行。 */ public static <T> void method( T w ){ System.out.println(w); }}public class GenericDemo4{ public static void main(String[] args) { Test<String> t = new Test<>(); t.print("aaa"); t.print(123); t.print(new Object());
Test.method("bbb"); }}
泛型接口和泛型传递
- 泛型接口,即:把泛型定义在接口上,泛型接口。
- 把泛型定义在类上,创建这个类对象的时候,由具体的创建者来明确具体的泛型类型。
- 定义在接口上,接口是要有实现类来实现这个接口。这时就可以在实现这个接口的时候明确具体的泛型。
- 当在定义一个类实现某个接口的时候,如果接口上有泛型,但是当前这个实现并不能明确具体的类型时,这时这个实现类可以继续把泛型往下传递,最后后具体创建这个实现类对象的使用这个类明确具体的泛型。这个技术称为泛型的传递。
- 定义泛型接口
interface Inter<W>{ public void show(W w);}
- 实现类实现泛型接口
/* * 在书写接口的实现类,就是在使用接口,这时可以明确接口上的泛型类型 */class InterImpl implements Inter<String>{ @Override public void show(String w) { System.out.println(w); }}
- 泛型的传递
//interface Collection<E>{}//interface List<E> extends Collection<E>{}//class ArrayList<E> implements List<E>{}/* * 在定义实现类的时候,如果不知道接口中泛型的具体类型时,可以继续使用泛型 */class InterImpl2<W> implements Inter<W>{ public void show(W w) { }}
public class GenericDemo5{ public static void main(String[] args) { InterImpl2<String> in = new InterImpl2<>(); }}
泛型通配符
使用一个符号来匹配所有的数据。这个符号就被称为通配符。
- 需求:
定义功能,专门用来打印集合中的数据
* 打印集合,就是使用遍历,取出集合中的每个元素,然后打印在屏幕上即可。
public class GenericDemo7 { public static void main(String[] args) { ArrayList<String> al = new ArrayList<String>(); Collections.addAll(al, "aaaa","dddd","rrrrr","sssss","itcast","ABCD"); printCollection(al); LinkedList<Integer> ll = new LinkedList<Integer>(); ll.add(123); ll.add(333); ll.add(444); ll.add(5555); printCollection(ll); } /* * 定义功能负责打印Collection集合中的元素 * 这个方法要打印的Collection集合中的元素,这时并不知道真正传递进来的容器中存放的是什么类型的元素。 * 在定义功能的时候,无法明确Collection的泛型类型。 * 这时可以使用泛型中的通配符来代替。 * Java中泛型的通配符可以使用?来表示 * Collection<?> coll 当前这个方法可以接收Collection下的所有子集合。?表示的是子集合中的元素类型, * 当传递的子集合中存放的是什么类型,?就代表什么类型 */ public static void printCollection( Collection<?> coll ){ for (Iterator<?> it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } } /* public static void printCollection( ArrayList<String> coll ){ for (Iterator<String> it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } } public static void printCollection( LinkedList<Integer> coll ){ for (Iterator<Integer> it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } } */}
泛型上下限
/* * 泛型的限定 */public class GenericDemo6 { public static void main(String[] args) { //这个容器中存放Student对象 ArrayList<Student> al = new ArrayList<Student>(); al.add(new Student("zhangsan",23)); al.add(new Student("lsi",23)); al.add(new Student("wangwu",23)); al.add(new Student("zhaoliu",23)); al.add(new Student("zhangsan",23)); printCollection(al); //这个容器中存放Person对象 ArrayList<Person> al2 = new ArrayList<Person>(); al2.add(new Person("zhangsan",23)); al2.add(new Person("lsi",23)); al2.add(new Person("wangwu",23)); al2.add(new Person("zhaoliu",23)); al2.add(new Person("zhangsan",23)); printCollection(al2); //这个容器中存放Worker对象 LinkedList<Worker> ll = new LinkedList<Worker>(); ll.add(new Worker("xiaoqiang",33)); ll.add(new Worker("wangcai",32)); ll.add(new Worker("huaan",28)); ll.add(new Worker("qiuxiang",18)); //printCollection(ll); } /* * 这个方法Collection<?> coll 它可以接收Collection下的所有子类,并且可以打印其中的数据 * 现在对这个方法进行限定,可以打印集合中的元素,但是要求集合中的元素必须是Person类型或者是Person的子类类型 * 这时需要在方法上加泛型的限定 * 泛型的上限限定 ? extends Person 这里表示?可以是Person本身,也可以是Person的子类类型 * 泛型的下限限定 ? super Student 这里?表示当前可以是 Student类型,也可以是Student的父类类型 */ public static void printCollection( Collection< ? extends Person > coll ){ for (Iterator<?> it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } }}
泛型在API中的应用
以TreeSet构造方法为例:
public class TreeSet<E> implements Set<E>{ public TreeSet(){}}
//可以接收一个集合作为创建TreeSet容器时的初始化数据
public TreeSet( Collection< ? extends E > c ){}
这个构造方法主要作用用于创建TreeSet集合,在创建这个集合的时候可以给其传递一个Collection下的子集合,作为TreeSet集合的创建时的初始数据。
TreeSet<...> set = new TreeSet<...>(...)
在创建TreeSet集合对象的时候,明确了泛型,那么就表示TreeSet定义的E泛型就被明确出来了。由于TreeSet集合要对其中的元素进行排序。
在明确的这个E一定是具备排序的功能。那么在创建TreeSet的时候,传递另外一个集合进来,就要求传递进来的这个集合中的元素一定要能够和E进行比较,然后进行排序。
Collection< ? extends E > 这里限定上限的原因是由于创建TreeSet集合的时候明确的类型E类型,那么给TreeSet集合对象初始数据的时候,初始的数据可以E本身,也可以是E的子类类型, 这个样就能够保证在TreeSet集合中存放下。
public
TreeSet(
Comparator<? super
E> comparator)
{
创建TreeSet集合的时候,可以传递比较器。比较器的功能是对集合中的元素进行比较的。
比较器就要从集合中把元素取出来,对其中的元素进行比较。比较器首先要从集合中把元素取出来,并且比较器就要使用响应的类型来接收取出的这些元素。
new TreeSet<E> (new Copmarator(){});
Collecions工具类中的方法
定义泛型 返回值类型 方法名 参数列表
public static <T extends Object & Comparable<? super T>> T max (Collection<? extends T> coll) {
<T extends Object & Comparable<? super T>> T这个类型必须是Object子类同时T类型还要实现Comparable接口
}
Java总结篇系列:Java泛型
泛型技术