黑马程序员技术交流社区

标题: 泛型中通配符存在的意义? [打印本页]

作者: 赵珏    时间: 2014-1-15 18:59
标题: 泛型中通配符存在的意义?
一直搞不懂泛型中为什么要用通配符? 有什么情况是一定要用到通配符才行的么? 还是说通配符是为了提供方便?
如果是提供方便的话, 提供了什么方便呢?

我写了个小程序里面包含了泛型方法的各种情况, 可是没有找到一种情况是一定要用通配符的. 是不是我试验的不全面?

  1. public class genericsTest {

  2.         public static void main(String[] args) {
  3.                 // TODO Auto-generated method stub
  4.                 Box<String> strBox = new Box<String>();
  5.                 Box<Integer> intBox = new Box<Integer>();
  6.                
  7.                 strBox.setElement("aa");
  8.                 intBox.setElement(123);
  9.                
  10.                 unbox1(strBox);
  11.                 unbox2(strBox);
  12.                 unbox3(strBox);
  13.                
  14.                 /*下面语句会报错, 因为intBox中的unbox4方法已经在定义对象时就被参数化了
  15.                 intBox.unbox4(strBox);*/
  16.                
  17.                 strBox.unbox4(strBox);
  18.                 intBox.unbox5(strBox);
  19.                 intBox.unbox6(strBox);

  20.         }
  21.        
  22.         //不用通配符的方法 会报警告
  23.         private static void unbox1(Box box){
  24.                 System.out.println(box.getElement());
  25.         }
  26.        
  27.         //用泛型参数的方法
  28.         private static <K> void unbox2(Box<K> box){
  29.                 System.out.println(box.getElement());
  30.         }
  31.        
  32.         //用通配符的方法
  33.         private static void unbox3(Box<?> box){
  34.                 System.out.println(box.getElement());
  35.         }
  36.         // Box<?> 是一个参数类型, <?> 是这个参数类型的一部分. <T>只能用来表示一整个类型.
  37.        

  38. }



  39. class Box<T>{
  40.         private T element;

  41.         public T getElement() {
  42.                 return element;
  43.         }

  44.         public void setElement(T element) {
  45.                 this.element = element;
  46.         }
  47.        
  48.         //泛型对象中的泛型方法
  49.         public void unbox4(Box<T> box){
  50.                 System.out.println(box.getElement());
  51.         }
  52.        
  53.         //泛型对象中用独立泛型参数的方法
  54.         public<K> void unbox5(Box<K> box){
  55.                 System.out.println(box.getElement());
  56.         }
  57.        
  58.         //泛型对象中用通配符的方法
  59.                 public void unbox6(Box<?> box){
  60.                         System.out.println(box.getElement());
  61.                 }

  62.        
  63. }
复制代码


除了注释起来的那个错误以外, 其余六个unbox方法的调用的结果都是正确的.
作者: 长石    时间: 2014-1-15 19:05
通配符修饰相当于声明了一种变量,它可以作为参数在方法中传递。这么做带来的好处就是我们可以将应用于包含某些数据类型的列表的方法也应用到包含其子类型的列表中。相当于可以在列表中用到一些面向对象的特性。
作者: DOOR    时间: 2014-1-15 20:07
本帖最后由 DOOR 于 2014-1-15 20:09 编辑
  1. public class Generic {
  2.         public static void main(String[] args) {
  3.                 //因为show方法是用List<?>通配符接收的,所以可以是任意类型!
  4.                 List<String> l1 = new ArrayList<>();//new ArrayList<String>()
  5.                 show(l1);
  6.                 List<Double> l2 = new ArrayList<>();
  7.                 show(l2);
  8.                 List<Number> l3 = new ArrayList<>();
  9.                 show(l3);        
  10.                 List<Object> l4 = new ArrayList<>();
  11.                 show(l4);
  12.                 //使用up方法的话接收类型为Number或者其子类
  13.                 //up(l1);//错误,因为up方法接收类型为Number或者其子类,l1(String)不符合!
  14.                 up(l2);
  15.                 up(l3);
  16.                 //使用down方法的话接收类型为Number或者其父类
  17.                 //down(l2);error
  18.                 down(l3);
  19.                 down(l4);
  20.         }
  21. public static void down(List<? super Number> l){
  22.                
  23.                 for (Object object : l) {
  24.                         System.out.println(object);
  25.                 }
  26.         }
  27. public static void up(List<? extends Number> l){
  28.                
  29.                 for (Object object : l) {
  30.                         System.out.println(object);
  31.                 }
  32.         }
  33.         public static void show(List<?> l){
  34.                
  35.                 for (Object object : l) {
  36.                         System.out.println(object);
  37.                 }
  38.         }
  39. }
复制代码


泛型中的通配符:通配符 ? 应用于具体类型不确定的时候;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
泛型限定:
上限:?extends E:可以接收E类型或者E的子类型对象。
下限:?super E:可以接收E类型或者E的父类型对象。
上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。
下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。

作者: 赵珏    时间: 2014-1-15 20:11
经过上面的哥们的提醒, 终于明白了.
就例如通配符的可以用来解决泛型中子父类之间无关的问题. 用通配符帮助限定边界就能模拟面向对象中继承关系了.
哈哈, 谢啦! 代码贴上来
  1.         Collection<String> collection1 = new ArrayList<String>();
  2.                 Collection<Object> collection2 = new ArrayList<Object>();
  3.                
  4.                 //下面语句不能成功编译, 虽然Object 是 String的父类, 可是泛型中子父类之间没有相关性
  5.                 /*collection2 = collection1;*/
  6.                
  7.                 Collection<? extends Object> collection3 = new ArrayList<String>();
  8.                 Collection<Object> collection4 = new ArrayList<Object>();
  9.                
  10.                 //这句话编译器不会报错, 通过通配符的帮助就能实现继承关系了
  11.                 collection3 = collection4;
复制代码

作者: 赵珏    时间: 2014-1-15 20:26
DOOR 发表于 2014-1-15 20:07
泛型中的通配符:通配符 ? 应用于具体类型不确定的时候;当操作类型时,不需要使用类型的具体功能时,只 ...

可是这个例子中定义的方法也不必非要用通配符啊? 用泛型参数的形式不行么? 比如
  1. public static<E extends Number> void up(List<E> l){
  2.                        
  3.                 for (Object object : l) {
  4.                         System.out.println(object);
  5.                 }
  6.         }


  7.         public static<T> void show(List<T> l){
  8.                
  9.                 for (Object object : l) {
  10.                         System.out.println(object);
  11.                 }
  12.         }
复制代码





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2