黑马程序员技术交流社区

标题: 关于泛型的问题(难道这题要郁郁而终了吗,求大神指导... [打印本页]

作者: 张俊迪    时间: 2013-6-11 19:01
标题: 关于泛型的问题(难道这题要郁郁而终了吗,求大神指导...
本帖最后由 张俊迪 于 2013-6-13 23:10 编辑

class GenericTest
{
    public static void main(String[] args)
    {
        ArrayList<Person> al=new ArrayList<Person>();
        al.add(new Person());
        al.add(new Person());
        show(al);
        ArrayList<Student>a1=new ArrayList<Student>();
        a1.add(new Student());
        a1.add(new Student());
        show(a1);
    }
    public static void show(Collection<? extends Person> c){
        Iterator<? extends Person> it=c.iterator();
        while(it.hasNext()){
            it.next().run();
        }
    }
}
class Person{
    void run(){
        System.out.println("Person");
    }
}
class Student extends Person{
    void run(){
        System.out.println("Student");
    }
}
对于上面的是正确的但是如果将show方法改正
    public static void show(Collection<? super Student> c){
        Iterator<? super Student> it=c.iterator();
        while(it.hasNext()){
            it.next().run();
        }
    }
编译将无法实现
但是如果在将上方法的
it.next().run()语句改成
System.out.println(it.next())编译能够通过,且输出对象引用地址,
请问这是为什么呢??????????纠结了一下午了


作者: 陈雪琪    时间: 2013-6-11 19:59
public static void show(Collection<? super Student> c){}
当楼主写的是? super Student时,表示可以接收Student和Student的父类类型,如果楼主看到了泛型限定应该知道吧。
? extends Person:则是可以接收Person类型或者Person的子类型。

所以楼主说会出错的那个方法中,编译时出错会发现提示的是在类Object中找不到符号run()方法。
可以这么想,? super Student可以接收任何Student和它的父类类型,Object是所有类的父类,当你使用  it.next().run();这个语句的时候,如果我们传入的是Object对象,很明显是没有run方法的,所有肯定就会报错了。

而你改成了这个,System.out.println(it.next()) 输出的就是这个对象的内存地址了,也就自然不存在调用特有方法的问题了。
作者: 张俊迪    时间: 2013-6-11 21:21
陈雪琪 发表于 2013-6-11 19:59
public static void show(Collection

你这意思?  super  E不就不能用了只能当子类中覆盖Object的方法才能用,要是那样的话,他们发明这个还有什么用呀,不可能就为了几个Object中的方法发明一个? super E吧,我感觉是不值
作者: 陈雪琪    时间: 2013-6-11 21:34
张俊迪 发表于 2013-6-11 21:21
你这意思?  super  E不就不能用了只能当子类中覆盖Object的方法才能用,要是那样的话,他们发明这个还有 ...

表示对这个也感觉挺奇怪的,毕竟Object是所有类的父类,不过好像super的确用的是不多。但是真就是这么个意思啊,等楼主看了泛型限定就知道了。 具体的呢 坐等牛人解答。
作者: 张俊迪    时间: 2013-6-11 21:52
陈雪琪 发表于 2013-6-11 21:34
表示对这个也感觉挺奇怪的,毕竟Object是所有类的父类,不过好像super的确用的是不多。但是真就是这么个 ...

我看了呀,老师说的是? super E 是指可接收E类型或者E的父类型,是下限,难道说这就话还有别的理解方式,我看了十多个看了这问题就你一个给力的,难道大神都睡觉去了,有点早呀
作者: 陈雪琪    时间: 2013-6-11 21:57
张俊迪 发表于 2013-6-11 21:52
我看了呀,老师说的是? super E 是指可接收E类型或者E的父类型,是下限,难道说这就话还有别的理解方式 ...

我说的那个不能算是别的理解方式,我自己是这么理解的。:L不知道是不是错误了,不过看编译出错的提示来说应该理解没错。只能说我是用更好理解的方式告诉你了。我也是菜鸟一枚呀。。
大神们应该这个点还没来逛论坛呢吧。。
作者: 张俊迪    时间: 2013-6-11 22:06
陈雪琪 发表于 2013-6-11 21:57
我说的那个不能算是别的理解方式,我自己是这么理解的。不知道是不是错误了,不过看编译出错的提示来说 ...

坐等大神吧,纠结的题呀
作者: 刘凯    时间: 2013-6-11 22:18
这个问题 以前在那个帖子见过一下  <? super Student> 表示可以接收  Student类型和其父类 ,但是 会默认将传入的对象提升为Object   不过取出的时候应该是能够强制向下转型, 也正是这个原因,  Object没有run这个方法, 所以调用run() 自然是不能够通过的,这个可用反射的方法去验证,next()出来的对象是不是Object类  ,可以自个写个demo验证一下。
作者: 王鑫    时间: 2013-6-11 22:37
本帖最后由 王鑫 于 2013-6-11 22:46 编辑

正好也学到这块,也纠结了好一段时间。 反复看了多遍毕老师的视频,最后发现,老师讲?super E是针对API文档说的。
? super E表示的是你可以在该处接收E或者E的父类,但并不是在程序代码上写? super E。
你仔细再看一遍视频就会发现,老师在Comparable<  >中写了Student和Person,但并没有把? super Student 写进去。



作者: 张俊迪    时间: 2013-6-11 22:53
王鑫 发表于 2013-6-11 22:37
正好也学到这块,也纠结了好一段时间。 反复看了多遍毕老师的视频,最后发现,老师讲?super E是针对API文 ...

我知道写成Person是对的,可是他在视频上说? super E是获得E及其父类型的,是下限,这句怎么解释,也不能他怎么写我们就怎么写,应该需要变通一下吧,再说人家API中都说能用了,这证明这方法是可行的,只是没找错的地方
作者: 张俊迪    时间: 2013-6-11 22:57
刘凯 发表于 2013-6-11 22:18
这个问题 以前在那个帖子见过一下

这个我今下午测试了,出来的引用对象地址分别是Student的和Person的,反射出来的是Student和Person,你要是不信你可以试试,要么就是我反射错了,你要是反射出Object给我看看,长长见识
作者: 刘凯    时间: 2013-6-11 23:48
张俊迪 发表于 2013-6-11 22:57
这个我今下午测试了,出来的引用对象地址分别是Student的和Person的,反射出来的是Student和Person,你要 ...

嗯, 我也试了下,反射出来确实是原来的类型,  不过确实在以前的一个帖子上见过,DOS 命令行里出现错误, 说是Object没有相关方法,也就是说 把存入的类型提升为Object了,,, 那就  等大神出现了就
作者: 不破大地    时间: 2013-6-11 23:49
本帖最后由 不破大地 于 2013-6-12 00:00 编辑

楼主,不对啊,没改之前也是编译失败 ,错误提示为:The method run() from the type Person is not visible
从结果上看是格式不对哈,不管是原来的show内部代码还是更改后的代码,只要将其转换成person类引用便可:((Person) it.next()).run();
查看了下API文档后,我觉着是因为:iterator迭代出来的是一个元素,元素应该不可以直接调用原来的方法,而是要将其转换成具体的类型后在调用,
又因为student继承了person,并覆盖了run方法,所以当返回student对象元素时,父类引用指向了子类对象,故根据多态,编译时时person的run方法
但是实际运行时调用的为student的方法(编译看左边,运行看右边)。
打印的结果为:
Person
Person
Student
Student
补充一句:我也纠结了好久。。。

作者: 张俊迪    时间: 2013-6-11 23:57
刘凯 发表于 2013-6-11 23:48
嗯, 我也试了下,反射出来确实是原来的类型,  不过确实在以前的一个帖子上见过,DOS 命令行里出现错误 ...

都快十二点了,等大神呀!!!!!!!
作者: 张俊迪    时间: 2013-6-11 23:59
不破大地 发表于 2013-6-11 23:49
楼主,不对啊,没改之前也是编译失败 ,错误提示为:The method run() from the type Person is not visible ...

不会吧,我的怎么能编译成功呢
作者: 不破大地    时间: 2013-6-12 00:04
张俊迪 发表于 2013-6-11 23:59
不会吧,我的怎么能编译成功呢

刚刚又修改了下,你看看咯,我用的是myeclipse,自动提示的,而且你的代码我是完全复制过去的。
作者: 张俊迪    时间: 2013-6-12 00:13
不破大地 发表于 2013-6-12 00:04
刚刚又修改了下,你看看咯,我用的是myeclipse,自动提示的,而且你的代码我是完全复制过去的。 ...

我用的记事本写的,在dos下运行的,我的可以
作者: 张俊迪    时间: 2013-6-12 00:15
不破大地 发表于 2013-6-11 23:49
楼主,不对啊,没改之前也是编译失败 ,错误提示为:The method run() from the type Person is not visible ...

编译左边,运行看右边确实不错,可是我已经加上泛型了,已经限定了,这不是主要问题的所在,要不你好好看看泛型,发现新大陆回我一下
作者: 黎志文    时间: 2013-6-13 08:16
1. Collection<? extends Person> c :表示该集合里面,能够接收的元素只能是Person类或其子类的对象。当Person类里面有run()方法时,那么其子类里面,也必定有该方法,那么你去迭代该集合中的元素时,这些元素因为是Peron类或者其子类对象,必定都是会有run()方法,所以it.next().run()是没有问题的;

2. Collection<? super Student> c:表示该集合里面,能够接收的元素只能是Student类或其父类的对象,Student类中有run()方法,不一定代表其父类中就有该方法,故it.next().run()此时是不可行的。
3.  ? super Student这种方式,可用于函数中的形参,表示该函数的形式参数类型是Student或其父类。可通如下的例子来理解,当我们这样定义:
class Function<? super Student>
{
      int  fun(<? super Student> S)    //当类或接口上定义了泛型,那么该泛型可作用于整个类中的所有方法,
}
这表明,fun函数中的形参是Student或其父类,那么当我们调用fun方法,往该方法中传递的实参是Student类,就可以很好的通过,因为是父类引用指向子类对象。这种用法常用于形参当中,因为定义形参的范围比实参的范围大,是可以通过的(多态)。
不知道这样讲,你能否理解?       
我也曾为泛型苦恼了好久,后面不断的看视频,自己去验证,花了挺长时间才明白过来,希望对你有所帮助。
作者: x378320002    时间: 2013-6-13 10:00
黎志文 发表于 2013-6-13 08:16
1. Collection

你这个理解的fun功能,要么不定义功能,要么只定义Object里的功能啊,是不是等着别人用时再传参数去覆盖啊?
作者: x378320002    时间: 2013-6-13 10:12
楼主我来终结你的疑惑吧,相信毕老师讲的,添加时用上限,取出时用下限,这句没理解吧,理解了就很好判断了。
正确的代码运行结果不说了,懂多态就很好解释了,我只说错误的这个。
public static void show(Collection<? super Student> c){
        Iterator<? super Student> it=c.iterator();
        while(it.hasNext()){
            it.next().run();
        }
    }
其实原因就是二楼那美女说的原因,你不知道?里有没有run,调用run当然就错了,好吧再具体点就是,
改成((Person)it.next()).run();编译就能通过,因为调用方法之间明确了类型,里面有run,但是这样就失去泛型
的意义了,因为如果传入object类,编译也能通过,但是运行失败。
你说这里不能用下限,当然不能用下限,因为这里是往集合里面添加元素呢。下限还有什么用呢,好吧这和object里面有多少方法无关,即使object没有方法,
下限依旧很有用,下限是在从集合取元素时用的,很有用。说起来有好多人不理解,为了让更多人看到我再开个贴吧,你去我帖子里看。
作者: 黎志文    时间: 2013-6-13 10:14
x378320002 发表于 2013-6-13 10:00
你这个理解的fun功能,要么不定义功能,要么只定义Object里的功能啊,是不是等着别人用时再传参数去覆盖 ...

其实就是:定义函数中的形参时,范围要大,那么调用该函数,给形参传递实参时,能接收的实参范围就更广,比如说,你定义一个函数fun(Object obj){},该函数的形参因为是Object类型,所以它能接收一切类型的实参对象;而fun(<? super Student> s),那么因为该函数的形参是Student及其父类,就便于接收Student类的对象。
作者: HM张博文    时间: 2013-6-14 10:15
这个我不懂哦,楼下的来完成吧




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