A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 罗海云 中级黑马   /  2013-3-1 12:28  /  2518 人查看  /  7 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 罗海云 于 2013-3-1 15:23 编辑
  1. package com.itheima.p1.text;

  2. class Text{
  3. public static void main(String[] args){
  4. demo<String> d = new demo<String>();
  5. d.setName("a");
  6. System.out.println(d.getName());
  7. demo<?> d1 = d; // 为什么这句都成功了..
  8. System.out.println(d1.getName());//这儿也可以取..就是不能赋值..
  9. d1.setName("sss");//这儿设置不了值呢? 不是?通配符类似于Object的吗?
  10. //这是什么原理呢?大家来解释解释..

  11. }
  12. }
  13. class demo<T>{
  14. private T name;

  15. public T getName()
  16. {
  17. return name;
  18. }

  19. public void setName(T name)
  20. {
  21. this.name = name;
  22. }

  23. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
陈丽莉 + 1

查看全部评分

7 个回复

倒序浏览
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
但是为什么不能参与调用参数化有关方法的原理不知道。
回复 使用道具 举报
void printCollection(Collection<?> c){
回复 使用道具 举报
因为不知道d1是具体是什么类型的,所以不能去设置其值,一个未知的引用无论是作为变量还是形参都是“只读的",
d1.setName("sss");//public void setName(T name) 因为这里的T不知道具体是类型,所以这里会出错

void printCollection1(Collection<Object> c); //这里只能接收Collection<Object>类型的参数

void printCollection1(Collection<?> c);//这个就可以接收任意类型Collection参数

所以?与Object不类似吧,个人见解,
回复 使用道具 举报
本帖最后由 付玉光 于 2013-3-1 14:17 编辑

class Text3{
        public static void main(String[] args){
                demo<String> d = new demo<String>();
                d.setName("a");
                System.out.println(d.getName());
                //demo<?> d1 表示它可接收demo类中泛型类型为任意引用类型的demo对象的引用。
                //此处它的用法类似于多态
                demo<?> d1 = d;

                //这里可以取值,是因为该方法是与泛型类型参数无关的方法(该方法不用接收泛型类型参数)。
                //也就是说:你上面因事先就声明了它里面的类型,里面声明的什么类型,
                //我调用该方法时就返回什么类型就行了。
                System.out.println(d1.getName());

                //这里不能赋值是因为,d1中的泛型类型为不具体类型,
                //也就是说,连它自己都不知道它中的泛型类型是什么
                //具体类型(它不知道自己是什么类型的,并不表示你就可以像其中放任何引用类型),
                //你怎么能给它赋String类型的呢?
                d1.setName("sss");
        }
}
class demo<T>{
        private T name;
        public T getName()
        {
                return name;
        }
        public void setName(T name)
        {
                this.name = name;
        }
}

希望对你有帮助{:soso_e100:}

评分

参与人数 1技术分 +1 收起 理由
陈丽莉 + 1

查看全部评分

回复 使用道具 举报
额.谢谢楼上几位的解答..我懂了
回复 使用道具 举报
通配符的一个好处是允许编写可以操作泛型类型变量的代码,并且不需要了解其具体类型。
--------------------------------------------------
例如
demo<String> d = new demo<String>();
d.setName("a");
demo<?> d1 = d;
System.out.println(d1.getName());//这儿也可以取..就是不能赋值..
d1.setName("sss");//这儿设置不了值呢? 不是?通配符类似于Object的吗?
//这是什么原理呢?大家来解释解释..
--------------------------------------------------
其实上面语句能做许多工作:它能调用 get() 方法,并且能调用任何从 Object 继承而来的方法(比如 hashCode())。
它惟一不能做的事是调用 set() 方法,这是因为在不知道该 Demo 实例的类型参数 T 的情况下它不能检验这个操作的安全性。
由于 d1 是一个 Demo<?> 而不是一个原始的 Demo,编译器知道存在一些 T 充当 demo 的类型参数,
但由于不知道 T 具体是什么,您不能调用 set() ,因为不能检验这么做不会违反 Demo 的类型安全限制
(实际上,您可以在一个特殊的情况下调用 set():当您传递 null 字母时。我们可能不知道 T 类型代表什么,但我们知道 null 字母对任何引用类型而言是一个空值)。
--------------------------------------------------
关于 d1.get() 的返回类型,d1 了解哪些内容呢?

它知道 demo.get() 是某些未知 T 的 T,因此它可以推断出 get() 的返回类型是 T 的擦除(erasure),
对于一个无上限的通配符就是 Object。因此上面的表达式 d1.getName() 具有 Object 类型,可以打印a。
--------------------------------------------------
d1.setName("sss"):为什么报错?(“capture#1 of ?”)

“capture#1 of ?” 表示什么?
当编译器遇到一个在其类型中带有通配符的变量,
比如 Demo<?> d1,它认识到必然有一些 T ,对这些 T 而言 d1 是 Demo<T>。
它不知道 T 代表什么类型,但它可以为该类型创建一个占位符来指代 T 的类型。
占位符被称为这个特殊通配符的捕获(capture)。
这种情况下,编译器将名称 “capture#1 of ?” 以 d1 类型分配给通配符。
每个变量声明中每出现一个通配符都将获得一个不同的捕获,因此在泛型声明 foo(Pair<?,?> x, Pair<?,?> y) 中,编译器将给每四个通配符的捕获分配一个不同的名称,因为任意未知的类型参数之间没有关系。

错误消息告诉我们不能调用 Set(),因为它不能检验 set() 的实参类型与其形参类型是否兼容:
因为形参的类型是未知的。在这种情况下,由于 ? 实际表示 “?extends Object” ,
编译器已经推断出 d1.get() 的类型是 Object,而不是 “capture#1 of ?”。
它不能静态地检验对由占位符 “capture#1 of ?” 所识别的类型而言 Object 是否是一个可接受的值。
回复 使用道具 举报
--------------------------------------------------
说白了就是demo<?>这种定义的泛型中调用带泛型参数并且要去改变其内部值的方法,在调用时都不知道自己内部值的原类型了。
不过你怎么会这么用呢?
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马