黑马程序员技术交流社区

标题: 泛型限定2非常不理解 [打印本页]

作者: Jim-剣◆﹏    时间: 2013-10-18 00:10
标题: 泛型限定2非常不理解
本帖最后由 Jim-剣◆﹏ 于 2013-10-18 21:41 编辑
  1. import java.util.*;
  2. class GenericDemo7
  3. {
  4.         public static void main(String[] args)
  5.         {
  6.                
  7.                 TreeSet<Student> ts = new TreeSet<Student>(new Comp());

  8.                 ts.add(new Student("abc03"));
  9.                 ts.add(new Student("abc02"));
  10.                 ts.add(new Student("abc06"));
  11.                 ts.add(new Student("abc01"));
  12.                
  13.                 Iterator<Student> it = ts.iterator();

  14.                 while(it.hasNext())
  15.                 {
  16.                         System.out.println(it.next().getName());
  17.                 }
  18.                 /**/



  19.                 TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());

  20.                 ts1.add(new Worker("wabc--03"));
  21.                 ts1.add(new Worker("wabc--02"));
  22.                 ts1.add(new Worker("wabc--06"));
  23.                 ts1.add(new Worker("wabc--01"));


  24.                 Iterator<Worker> it1 = ts1.iterator();

  25.                 while(it1.hasNext())
  26.                 {
  27.                         System.out.println(it1.next().getName());
  28.                 }
  29.         }
  30. }

  31. /*
  32. class StuComp implements Comparator<Student>
  33. {
  34.         public int compare(Student s1,Student s2)
  35.         {
  36.                 return s1.getName().compareTo(s2.getName());
  37.         }
  38. }

  39. class WorkerComp implements Comparator<Worker>
  40. {
  41.         public int compare(Worker s1,Worker s2)
  42.         {
  43.                 return s1.getName().compareTo(s2.getName());
  44.         }
  45. }
  46. */

  47. class Comp implements Comparator<Person>
  48. {
  49.         public int compare(Person p1,Person p2)
  50.         {
  51.                 return p2.getName().compareTo(p1.getName());
  52.         }
  53. }


  54. class Person
  55. {
  56.         private String name;
  57.         Person(String name)
  58.         {
  59.                 this.name = name;
  60.         }
  61.         public String getName()
  62.         {
  63.                 return name;
  64.         }
  65.         public String toString()
  66.         {
  67.                 return "person :"+name;
  68.         }
  69. }

  70. class Student extends Person
  71. {
  72.         Student(String name)
  73.         {
  74.                 super(name);
  75.         }

  76. }

  77. class Worker extends Person
  78. {
  79.         Worker(String name)
  80.         {
  81.                 super(name);
  82.         }
  83. }
复制代码
首先给毕老师视频中的原代码
泛型限定有个概念


Class person{}
Class student extends person{}
假设student是Person的子类,person是父类
如果只想要接收personperson的子类,那么可以给一个泛型限定<? extends person>
如果只想要接收student和它的父类型,那么可以给一个泛型限定<? super student>
api文档中,TreeSet的构造方法里传入的一个带泛型限定的比较器

我要怎么理解里面的这个泛型限定?
先说说毕老师的说法,老师选中图片中划了红线的区域的E说,TreeSet里存的是worker,这里就写worker,TreeSet里存的是Student,这里就写Student,这样很麻烦,然后毕老师说可以写他们的父类型,即Person,那么这个比较器就可以接收Person和person的子类对象了
毕老师视频中写的比较器的泛型限定就是
  1. class Comp implements Comparator<Person>
  2. {
  3.         public int
  4. compare(Person p1,Person p2)
  5.         {
  6.                 return
  7. p2.getName().compareTo(p1.getName());
  8.         }
  9. }
复制代码
那这样就可以比较同时比较Person对象,Person的子类Student对象,Person的子类worker对象,这我明白
但是,TreeSet的构造方法TreeSet(Comparator<? super E > comparator )里面的这个参数泛型限定该怎么理解
我的理解是比较器的泛型限定传入了Person,比较器默认把Person传入替代了E,变成Comparator<? super Person>,但是根据<? super Person>的概念,就说明,这个比较器可以接收Person和person的父类,但明显person本身就是父类了,不对啊
如果不是这样理解,那为什么不像下面这样定义比较器,而要像上面那样定义比较器呢?
  1. class Comp implements Comparator<? super Person>
  2. {
  3.         public int compare(Person p1,Person p2)
  4.         {
  5.         return p2.getName().compareTo(p1.getName());
  6.         }
  7. }
复制代码

作者: Jim-剣◆﹏    时间: 2013-10-18 16:07
没有人看懂吗?
作者: 赖波    时间: 2013-10-18 19:47
TreeSet(Comparator<? super E > comparator )的意思是比较器可比较本身的和父类的都可
  1. import java.util.Comparator;
  2. import java.util.Iterator;
  3. import java.util.TreeSet;

  4. public class Test {
  5.         class Person {
  6.                 private String name;

  7.                 public String getName() {
  8.                         return name;
  9.                 }
  10.                 public void setName(String name) {
  11.                         this.name = name;
  12.                 }
  13.         }
  14.         class Student extends Person{
  15.                 void study(){
  16.                         System.out.println("学习");
  17.                 }
  18.         }
  19.         /*
  20.          * TreeSet(Comparator<? super E > comparator )的意思是比较器可比较本身的和父类的都可,如下例:泛型Student
  21.          * 可用,比的是父类的比较器CompP,和本身的CompS比较器
  22.          * 下面为测试:
  23.          * */
  24.         public static void main(String[] args) {
  25.                 Test test=new Test();
  26.                 TreeSet<Person> t1 = new TreeSet<Person>(test.new CompP());//test.newCompS()子类比较器不行
  27.                 Person p1=test.new Person();
  28.                 p1.name="张三";
  29.                 t1.add(p1);
  30.                 /*加入子类对象,将不能比较,所以加不了,加入则报错,必定*/
  31.                 //Student s1=test.new Student();
  32.                 //t1.add(s1);
  33.                 Iterator<Person> it1=t1.iterator();
  34.                 while(it1.hasNext()){
  35.                         System.out.println("Person父类TreeSet:"+it1.next().getName());
  36.                 }
  37.                
  38.                 TreeSet<Student> t2=new TreeSet<Student>(test.new CompP());//当然用子类比较器也可
  39.                 Student s2=test.new Student();
  40.                 s2.setName("李四");
  41.                 t2.add(s2);
  42.                 Iterator<Student> it2=t2.iterator();
  43.                 while(it2.hasNext()){
  44.                         System.out.println("Student子类TreeSet:"+it2.next().getName());
  45.                 }
  46.         }

  47.         class CompP implements Comparator<Person> {//父类比较器
  48.                 public int compare(Person p1, Person p2) {
  49.                         return p2.getName().compareTo(p1.getName());
  50.                 }
  51.         }
  52.         class CompS implements Comparator<Student> {//子类比较器
  53.                 public int compare(Student s1, Student s2) {
  54.                         return s2.getName().compareTo(s1.getName());
  55.                 }
  56.         }
  57. }
复制代码

作者: 阿里策    时间: 2013-10-18 20:18
本帖最后由 阿里策 于 2013-10-18 20:20 编辑

    我的理解是,对于TreeSet(Comparator<? super E> comparator),这里Comparator<? super E>中<>肩括号里面传入的是E或者是E的父类,使用时都可以接收E的对象。
    所以,你的代码class Comp implements Comparator<Person>,肩括号中传入的是Person,也就是说传入的是Student的父类,所以可以接收到Student的对象。同理,传入的Person也是Worker的父类,所以也可以接收到Worker的对象。
    对于TreeSet(Collection<? extends E> c) ,肩括号中传入的是E 或者是E的子类,使用中都可以接受E的对象。所以你可以验证:当TreeSet使用集合泛型时,传入的是Worker或者Student时,都是可以接收到Person对象的。
   
作者: Jim-剣◆﹏    时间: 2013-10-18 21:15
本帖最后由 Jim-剣◆﹏ 于 2013-10-18 21:39 编辑
阿里策 发表于 2013-10-18 20:18
我的理解是,对于TreeSet(Comparator

昨晚想到现在,经你一点就明白,瞬间豁然开朗。之前思维存在定势



作者: 李江    时间: 2013-10-18 21:23
楼主,如果你的问题解决了就把帖子状态改为提问结束吧,谢谢了
作者: 赖波    时间: 2013-10-18 21:36
本帖最后由 赖波 于 2013-10-18 21:47 编辑

楼主我建议你写代码。TreeSet的构造函数加参:Comparator和Collection两个的用法。你会明白的。
作者: 寻丶丶觅    时间: 2013-10-19 22:00
我的理解是,对于TreeSet(Comparator<? super E> comparator),意思就是其可以接受的可以是本身的比较方法或者其父类的比较方法,对于老毕的讲解就是你可以选择Student的比较方法,也可以采用其父类的比较方法。
对于TreeSet(Collection<? extends E> c) ,是其接收对象为E或者E的子类。
作者: 斗胆潇洒    时间: 2013-10-20 00:44
本帖最后由 斗胆潇洒 于 2013-10-20 01:10 编辑

道友,看了这贴,我怎么觉得有点不对劲啊,泛型的上下限的定义是精辟的语句,按定义的那种理解才是正确的,
另外,对于TreeSet<E>一旦你明确了E,如String,那么add时只能添加String,不可能add一种类型,又add它的子或父类类型,
本身Integr和String就是Object的子类,加泛型定住一个类型,就是为了保证运行时安全性,所以我觉得别试图将<? super E>以add能添加不同子父类型来验证。还有,那个<? super E>就按定义那样理解就行了,即接收E或E的父类,只是没有写出那种例子而已,我试了试,这个应该可以
先我们来确定<? super E>中的E,如图:


当我们不写MyComp的<>时,它会有警告,你看一下就知道E是什么了,Sun是我定义的孙子类,是TreeSet<Sun>的,MyComp是个Comparator子类,例子:
import java.util.*;
public class TreeSetTest
{
                public static void main(String[] args)
        {   
                        //报错,我们发现Comparator<? super E>的格式将被填充为<Zi super Fu>,错误
                        //TreeSet<Fu> ts1 = new TreeSet<Fu>(new MyComp<Zi>());
                        
                        //正确,Comparator<? super E>填充为<Fu super Sun>和<Zi super Sun>
                        TreeSet<Sun> ts1 = new TreeSet<Sun>(new MyComp<Fu>());
                        TreeSet<Sun> ts11 = new TreeSet<Sun>(new MyComp<Zi>());
//add()方法的传入类型是为<Sun>的,这是为了统一,以保运行时安全,如果可以连着添加含有继承关系的多个类型对象,在运行时不安全的                        
                        //报错,Collection<? extends E>的格式,将被填充为<Fu extends Zi>,错误
                        //TreeSet<Zi> ts2 = new TreeSet<Zi>(new ArrayList<Fu>());

                        //正确,Collection<? extends E>的格式,被填充为<Zi extends Fu>和<Sun extends Fu>
                        TreeSet<Fu> ts2 = new TreeSet<Fu>(new ArrayList<Zi>());
                        TreeSet<Fu> ts22 = new TreeSet<Fu>(new ArrayList<Sun>());
        }
}
class MyComp<T> implements Comparator<T>{
                public int compare(T o1, T o2) {
                        return 1;
                }
}
class Fu{}
class Zi extends Fu{
}
class Sun extends Zi{
}


看完了你的长篇,有想法的回复一下,分够,不求技术分, 只是咱俩交换了下思想的苹果,1+1 => 4个

作者: Jim-剣◆﹏    时间: 2013-10-21 00:03
斗胆潇洒 发表于 2013-10-20 00:44
道友,看了这贴,我怎么觉得有点不对劲啊,泛型的上下限的定义是精辟的语句,按定义的那种理解才是正确的, ...

哥们,非常感谢你分析得那么详细,和大家讨论这些疑惑,我的理解也加深
我也说说我现在的理解,和你交换一下
首先看看treeSet的API文档介绍
和传入比较器的treeSet构造方法TreeSet(Comparator<? super E> comparator)
和add(E e)方法

TreeSet<E>这里面的一个E代表着存入的对象的类型,和构造TreeSet(Comparator<? super E> comparator) 里比较器里的E是同一个E,和add(E e)里的也是同一个E,也就是说,假设Student继承Person,Person是父类,Student是子类,我再定义 一个和Person和Student无关的worker类,如果我用在创建TreeSet容器的时候,这样写TreeSet<Student> treeSet = new TreeSet<Student>(),那么此时E就已经确定了,E用Student替代了,上面说了比较器里的和add里的E是同一个E,那么API文档里比较器这样写Comparator<? super E>,是为了提示我们随着E确定,比较器里能接收的对象也就随之确定了,此时比较器只能这样写Comparator<? super Student>或者Comparator<Student>或者Comparator<Person>而不能写Comparator<Worker>
add(E e)就更加明确了,TreeSet<Student> treeSet = new TreeSet<Student>()--->add(student stu),TreeSet的泛型定义的接收什么类型就是为add()这种方法提供限定的。其实说白了,这里的E就好比数学上解题的时候设的一个未知数“X”,使用时“X”确定了,往方法里代入就可以了。
哥们进黑马了吗?

作者: 斗胆潇洒    时间: 2013-10-21 00:48
Jim-剣◆﹏ 发表于 2013-10-21 00:03
哥们,非常感谢你分析得那么详细,和大家讨论这些疑惑,我的理解也加深
我也说说我现在的理解,和你交换 ...

:D:handshake
呵呵,从api手册入手,有理有据,哥们真棒,加油,
我已被黑马26期录取,现在往后看着,顺带回答问题当复习,
有什么想要交流的,可以找我哦,黑马见:victory:
作者: Jim-剣◆﹏    时间: 2013-10-21 08:52
斗胆潇洒 发表于 2013-10-21 00:48
呵呵,从api手册入手,有理有据,哥们真棒,加油,
我已被黑马26期录取,现在往后看着,顺 ...

我准备27期就去考,28期去上,共勉!!!




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