Java中泛型的基础到提高《精简》
2015-08-16 17:56
405 查看
<span style="font-family: SimHei; background-color: rgb(255, 255, 255);"> </span><span style="font-family: SimHei; background-color: rgb(255, 255, 255);"> </span><span style="font-family:SimHei;font-size:24px;color:#ff0000;">泛型初级</span>
<span style="font-family: SimHei; background-color: rgb(255, 255, 255); font-size: 18px;"><span style="white-space:pre"> </span>泛型的由来:</span>
集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,
需要进行向下转型,如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常。
这样就给程序带来了不安全性。
在jdk1.5以后就有了解决方案——泛型技术:在存储元素时,就不允许存储不同类型的元素。
存储了就编译失败。 所以就需要在存储元素时,在容器上明确具体的元素类型,这其实和数组定义很像。
泛型的好处:
1)将运行时期的ClassCastException异常转移到了编译时期,进行检查,并以编译失败来体现。 这样有利于程序员尽早解决问题。(不会再运行中出现异常,在编译时候就能够发现错误)(泛型规定不能只能是自己的类型或者是子类)
2)避免了向下转型(强转)的麻烦。
代码说明一切:
package cn.hncu.generic; //泛型能够限制类型。 public class genericdemo1 { public static void main(String[] args) { Mygeneric<Worker> generic =new Mygeneric<Worker>(); generic.add(new Worker()); //generic.add(00); 自己定义了泛型,只能是Worker类型的 // generic.add(new Student());//同样的报错,只能添加相同类型的数据 System.out.println(generic); Mygeneric<Student> s=new Mygeneric<Student>(); s.add(new Student()); // s.add(new Worker());//编译通不过,出错,只能添加泛型定义的数据 } } class Mygeneric <QQ>{//自己定义泛型,随便写写,最重要的是<>里面的内容必须与下面的一致,内容随便写都可以 QQ obj; public void add(QQ obj){//由于前面限制,所以只能添加QQ类型的数据 System.out.println("add:"+obj); } public QQ out(){ return obj; } @Override public String toString() { return "Mygeneric [obj=" + obj + "]"; } } class Worker{ } class Student{ }
使用泛型的动机举例(以集合为例):
对集合中存放元素的类型进行限定,防止后期出错。如果限定某个集合只能存放Person类对象(因为后期会把元素取出来当作Person对象来处理),这样放其它类型的对象在编译时就会报错。相当于把一个类泛化成很多个不同类型(具体化,限定化)的类。泛型使用的代码如:
List<Person> persons = new ArrayList<Person>;
Map<String,String> m = new HashMap<String,String>;
注意:当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和方法调用。原因很简单,参数化的泛型是一些实例。静态成员是被类的实例和参数化的类所共享的,所以静态成员不应该有类型参数和他们关联。
当一个类要操作的引用数据类型不确定的时候,可以将该类型定义一个形参。用到的这类时,由使用者来通过传递类型参数的形式,来确定要操作的具体的对象类型。 什么时候使用泛型类呢?只要类中操作的引用数据类型不确定的时候,就可以定义泛型类。 有了泛型类,省去了曾经的强转和类型转换异常的麻烦。
部分代码:
class Mygen<E>{ public void add(E e){ System.out.println(e.toString()); } public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全 return obj; } public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。 System.out.println("myout:" +a); return a; } // //静态方法带泛型,泛型一定要独立于类,因为它没有对象。 public static <E> E myout1(E a){ System.out.println("myout:" +a); return a; }
完整简单代码演示《细节都在代码解释中》:
package cn.hncu.generic;当某些类用的接口泛型时候,我们也能解决,要求返回和类一样和不一样的类型的泛型都能解决,详见线面代码:
public class genericdemo2 {
public static void main(String[] args) {
Mygen<String > my =new Mygen<String>();
my.add("abc");
//my.add(21);//编译错误,泛型规定只能使用String类型的
String s=(String) my.out("asd");//方法不带泛型,返回值需要强转,不是很安全
//返回值也必须是泛型类型; 泛型很安全
//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
int m=my.myout(11);//myout:11;返回值和传进去的参数类型必须一致;
char c=my.myout1('a');
}
}
class Mygen<E>{ public void add(E e){ System.out.println(e.toString()); } public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全 return obj; } public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。 System.out.println("myout:" +a); return a; } // //静态方法带泛型,泛型一定要独立于类,因为它没有对象。 public static <E> E myout1(E a){ System.out.println("myout:" +a); return a; }
}
package cn.hncu.generic;
public class genericdemo2 {
public static void main(String[] args) {
Mygen<String > my =new Mygen<String>();
my.add("abc");
//my.add(21);//编译错误,泛型规定只能使用String类型的
String s=(String) my.out("asd");//方法不带泛型,返回值需要强转,不是很安全
//返回值也必须是泛型类型; 泛型很安全
//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。
int m=my.myout(11);//myout:11;返回值和传进去的参数类型必须一致;
char c=my.myout1('a');
}
}
class Mygen<E>{ public void add(E e){ System.out.println(e.toString()); } public Object out(Object obj){//方法不带泛型,当返回时候需要强转,不是很安全 return obj; } public <A> A myout(A a){//方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。 System.out.println("myout:" +a); return a; } // //静态方法带泛型,泛型一定要独立于类,因为它没有对象。 public static <E> E myout1(E a){ System.out.println("myout:" +a); return a; }
}
泛型高级:
泛型的通配符:?
当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法,
这时泛型就用通配符?来表示即可。(助理解的比方: 泛型中的多态应用)。
当我们的集合中输出的两个集合是父子关系,就可以采用通配符输出(最简单的比喻):代码如下
package cn.hncu.generic; import java.util.ArrayList; import java.util.Iterator; public class genericPrint { public static void main(String[] args) { ArrayList<String> list=new ArrayList<String>(); list.add("qwe"); list.add("qw1e"); list.add("qw2e"); list.add("qw3e"); ArrayList<Integer> list1=new ArrayList<Integer>(); list1.add(1); list1.add(2); list1.add(3); list1.add(4); list1.add(5); print(list); print(list1); } private static void print(ArrayList<?> list) {// Iterator<?> it =list.iterator(); while(it.hasNext()) { Object obj =it.next(); System.out.println(obj); } } //因为这是运行期的多态,而我们的泛型在编译期 // private static void print(ArrayList<Object> list) { // Iterator<Object> it=list.iterator(); // while(it.hasNext()){ // Object obj =it.next();编译通不过; // System.out.println(obj); // } // // } }还有就是用到上限与下限
演示上限或者下限的应用体现(以TreeSet容器为例):
TreeSet的构造方法:
TreeSet(Collection<? extends E> c)
TreeSet(Comparator<? super E> comparator)
一般情况下:
只要是往容器中添加元素时,使用上限。 ? extends E
只要是从容器中取出元素时,是用下限。 ? super E
如在如下代码中,person为父类,Studentdemo为子类,当要求两个一起输出时候,可以采用上限,当要求比较大小采用通用的比较器的进行比较就可以用到下限,因为所有的子类都可以采用服类对象进行比较,具体代码如下:
package cn.hncu.generic; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; public class genericTopanddown { public static void main(String[] args) { // extendsdemo();//上限演示 superdemo(); } private static void extendsdemo() { ArrayList<Person> col=new ArrayList<Person>(); col.add(new Person("aa", 12)); col.add(new Person("bb", 19)); col.add(new Person("cc", 14)); /* * boolean addAll(int index, Collection<? extends E> c) 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 */ //能够实现一次性输出具有继承关系的对象 ArrayList<Studentdemo> cols=new ArrayList<Studentdemo>();//泛型的上限,<? extends E> cols.add(new Studentdemo("ee", 15)); cols.add(new Studentdemo("ff", 16)); cols.add(new Studentdemo("gg", 17)); // col.addAll(cols);//这样写不安全,我们是已知二者存在的关系下,严谨必须在使用一个集合放相同的类型 //外加排序 用TreeSet TreeSet<Person> set=new TreeSet<Person>();//必须实现compare接口 set.addAll(col); set.addAll(cols); Iterator<Person> it=set.iterator();//迭代器遍历 while(it.hasNext()){ Person p=it.next(); System.out.println(p); } } //下限演示 private static void superdemo() { TreeSet<Studentdemo> set =new TreeSet<Studentdemo>(new sortByName());//做一个比较器,person的子类都能比较 set.add(new Studentdemo("aja", 11)); set.add(new Studentdemo("aja1", 12)); set.add(new Studentdemo("aja3", 1)); set.add(new Studentdemo("aja22", 10)); Iterator<Studentdemo> it =set.iterator(); while(it.hasNext()){ System.out.println(it.next()); } //只要是person 的子类或是自己类,都可以通过(new sortByName())进行比较。这就是下限 ? super E } } class Person implements Comparable<Person>{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int compareTo(Person o) { return this.age-o.age; } } class Studentdemo extends Person{ String name; int age; public Studentdemo(String name,int age){ super(name,age); } } //TreeSet中的按名字比较的比较器(persom)通用。 class sortByName implements Comparator<Person>{ public int compare(Person o1, Person o2) { return o1.toString().compareTo(o2.toString()); } }
相关文章推荐
- Mac 下安装配置jdk
- java中的动态代理
- Java NIO系列教程(八) SocketChannel
- Java中的自动装箱与拆箱
- Java学习之路0804<复习>(线程、注解)
- Myeclipse 经常loading descriptor for XXX ,然后卡死
- [leetcode-139]Word Break(java)
- (2)java堆内存
- Eclipse - JDK内存配置- 环境配置
- Java流式IO
- Java基础—super和this关键字
- Spring事务传播特性实例解析
- java初始化顺序
- Java功底篇系列-03-一些位运算符你会如何理解?
- Java虚拟机详解02----JVM内存结构
- hdu1276 Java水果
- 记票统计(java版整理)
- Java IO (上)
- Spring(Struts、Hibernate优势 + Spring IOC、AOP + Spring实际开发过程)
- (1)java虚拟机概念和结构图