Java中泛型的基础到提高《精简》

来源:互联网 发布:window 查看端口占用 编辑:程序博客网 时间:2024/05/17 00:18

<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;}@Overridepublic 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);//这样写不安全,我们是已知二者存在的关系下,严谨必须在使用一个集合放相同的类型//外加排序 用TreeSetTreeSet<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;}@Overridepublic 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;}@Overridepublic 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());}}



1 0
原创粉丝点击