Java 集合16 泛型(通配符/泛型限定)

泛型其实也是对元素或者说是,这个类要操作的对象的限定(其实跟数据类型差不多)
一:当在使用泛型类或者是接口时,传递的具体类型不确定,可以通过通配符(?)表示
理解:
有泛型的类/方法在定义时,带的泛型可以看作是形参。但是在使用时,就要确定具体的类型,这就好比实参。当“实参”无法确定时,就可以用通配符? 来代替,表示什么类型都可以。
代码:

package Collection;
import java.util.*;
//演示通配符
public class GenericDemo3_Fanxing {
    public static void main(String[] args){
        List<StudentP> list =  new ArrayList<StudentP>();
        list.add(new StudentP("LIhua",20));
        list.add(new StudentP("Liusan",25));
        list.add(new StudentP("Zhangsan",18));
        Print(list);

        Set<String> set = new HashSet<String>();
        set.add("hdiwhd1");
        set.add("hdiwhd2");
        set.add("hdiwhd3");
        set.add("hdiwhd4");
        Print(set);
    }
    public static void Print(Collection<?> c){
        for(Iterator<?> it= c.iterator();it.hasNext();){
            //集合中的元素类型未知,不能转换或者是其他的操作
            System.out.println(it.next().toString());
        }
    }
}

运行结果
name = LIhua age = 20
name = Liusan age = 25
name = Zhangsan age = 18
hdiwhd1
hdiwhd4
hdiwhd3
hdiwhd2
总结:
获取元素(操作元素)的方法,这个方法操作的对象是,这个集合(传入方法的是这个集合)对于集合 list 和 set 获取的方法都是一样的,为了增加代码的复用性,所以将其封装成函数,但是对于传入方法的集合中的元素类型(这是一个整体/集合是跟这个函数的形参,但是元素类型是这个方法的实参)类型不定,所以使用通配符,表示任何类型的元素都可以。其实这里也可以不使用泛型,但是在Collection 定义时,就带有泛型,所以带上泛型是最安全的。

在API中通配符的体现:
Collection 中的

boolean containsAll(Collection<?> c)  

按理来说应该是:

class Collecton<E>{
    boolean  containsAll(Collection<E> c);
}//要弄清楚API中的定义就要去查看这个函数的源码

通过查看源码可以知道,containsAll——>调用了 contanis ———>调用了equals
所以在contains 的底层依据的是 equals .
而 equals 的定义:equals ( Object obj)
所以能比较的类型是所有的对象,还有一点,在集合中获取的元素,只要你取出后不使用元素的特有性质,不进行类型的转换,都是Object 类的。
所以在Collection 这个类中,contains 这个方法传入的集合中所包含的元素是任意的,也可以这样理解,他操作的其实不是传入这个集合的元素(所以与E无关)

——————————————————————————————————————————
二:泛型的限制进行操作,想要对被打印的集合中的元素类型进行限制,只对一些指定的类型进行操作
承接上面的例子,在获取元素的方法中,我们使用了通配符,表示什么元素都可以,但是,在实际的操作中我们只需向这个函数中传递 StudentP 和 Worker 这两个类型的元素。此时就要对集合中的元素进行限制。通过他们有共同的父类进行限制。
修改后的代码:

public static void Print2(Collection<? extends Human> c){
     for(Iterator<?> it= c.iterator();it.hasNext();){
            //集合中的元素类型未知
            System.out.println(it.next().toString());
        }
}
Collection<? extends Human> c
//表示:集合中的元素仍然未知,但是,它一定是Human 的子类

这叫设置了上限
//
这叫做这里要注意一点: :表示的是 传入的仅是 Human 这个类型

//
通配符? 在API 中的存在

设置下限:

Collection<?   super class >
//表示:元素类型还是未知,但是它一定是 这个类(class) 的父类

泛型限制的下限设置,一般在比较器中比较常用:
原理介绍:在要使用比较器,比较之后再存储到集合中(TreeSet),两个元素再比较时,一个元素是将要存入的,另一个是从集合中拿出来的,那么在比较这个方法中,定义的集合中元素的类型,首先肯定是唯一的,其次,一定是比较的元素类型的父亲。

在API中的体现:
TreeSet 中的两个构造函数 TreeSet(E)

TreeSet(Collection<? extends E> c) 
//构造一个包含指定集合中的元素的新树集,根据其元素的 **自然排序进行排序** 。  
TreeSet(Comparator<? super E> comparator) 
//构造一个新的,空的树集,根据指定的**比较器**进行排序。  

TreeSet(Collection

  @Override
  public class Human implements Comparable<Human>//实现Comparable
    public int compareTo(Human  o) {//
        int temp= this.age-o.age;
        return temp==0?this.name.compareTo(o.name):temp;
    }
}
   public static void main(String[] args){
        Collection<Worker>  c = new ArrayList<Worker>();
      c.add( new Worker("Zhnagsan1",45));
      c.add(new Worker("Zhnagsan2",18));
      c.add(new Worker("Zhnagsan3",20));

        TreeSet<Human> t = new TreeSet<Human>(c);

        //获取元素
        for(Iterator<Human> it = t.iterator();it.hasNext();){
            Human h = it.next();
            //TreeSet 中的泛型限定为Human   所以在获取的时候存入,Human  的子类也是完全不会有问题的
            System.out.println(h.toString());
        }
    }

TreeSet(Comparator

//定义一个构造器//按照名字的长度比较
class CompareByLengthName implements Comparator<Human>{//实现Comparator
    @Override
    public int compare(Human o1, Human o2) {
        int temp = o1.getName().length()-o2.getName().length();
        return temp==0? o1.getAge()-o2.getAge():temp;
    }
}
 TreeSet<Worker> t2  = new TreeSet<Worker>(new CompareByLengthName());
 //泛型下限

——————————————————————————————————
泛型使用中的小细节:在使用泛型,保证左右一致

猜你喜欢

转载自blog.csdn.net/Stitch__/article/details/82561882