今天在百度上看到这么一个问题,大家来分析一下是什么原因public class Test {
    public static void go(List list)
    {
        list.add(1.1);
    }
    public static void main(String args[])
    {
        List<String> list=new ArrayList<String>();
        list.add("1");
        go(list);
        System.out.println(list.get(1)); 
    }
}
以上代码编译通过,运行出错,很好理解但public class Test {
    public static void go(List list)
    {
        list.add("1.1");
    }
    public static void main(String args[])
    {
        List<Integer> list=new ArrayList<Integer>();
        list.add(1);
        go(list);
        System.out.println(list.get(1)); 
    }
}
这段代码却可以通过编译也可以运行,何解?

解决方案 »

  1.   

    public static void go(List list) 
        { 
            list.add("1.1"); 
        } 
    这个方法里也要用泛型.
    public static void go(List<Integer> list) 
        { 
            list.add(1.1); 
        } 
      

  2.   

    public static void go(List list) 
        { 
            list.add("1.1"); 
        } 
    这样没问题啊
      

  3.   

    编译 和 运行 有的时候真的是按它java的规范都说不清楚为什么…… 我也遇到过类似的疑问…… 后来看开了…… 既然学的是java…… 那就不要追究那么深……
      

  4.   

    没有装箱,仔细看了下,问题不是出在set的时候,而是get的时候,报错是System.out.println(list.get(1)); 这句,两个程序在这之前都是可以的,也就是说调用go()方法都是好的,不管外面传进来的List是什么泛型,只要这个方法的参数没有泛型限制,set的类型就没有限制,但是为什么取的时候,第一个报错,第二个没有报错,原本以为是类型的构造,比如第一个程序,原本希望get出来的是String型,而取出来的却是Double型,而String型没有new String(Double)这样一个构造函数,所以不行,而第二个原本希望get出来的是Integer型,而取出来的是String型,而Integer型有new Integer(String)这样一个构造函数,好象如此,但又解释不通,因为这个String是1.1,事实上只能构造成Double型,望高手指点。
      

  5.   

            Integer i = new Integer("1.1");
            System.out.println(i.toString());这样子就会报错。Exception in thread "main" java.lang.NumberFormatException: For input string: "1.1"
    at java.lang.NumberFormatException.forInputString(Unknown Source)
    at java.lang.Integer.parseInt(Unknown Source)
    at java.lang.Integer.<init>(Unknown Source)
    at test.Test.main(Test.java:29)
      

  6.   

    关键在PrintWriter.println方法的重载。
    第一段代码掉用的是
    void println(String x)
    而list.get(1)返回的是一个Double对象,所以会报错。第二段代码掉用的是
    void println(Object x)
    list.get(1)返回的是一个String对象,而String类继承自Object,所以没问题。
      

  7.   

    楼上说的好象是正解,但这样我还是有个问题不明:
    我试过System.out.println(list.get(1)); 改成System.out.println(list.get(1).toString());或System.out.println((String)list.get(1)); 
    还是不行,而且即使把list.get(1).toString()或(String)list.get(1)单独拿出来执行也是错的,除非:
    Object o = list.get(1);
    System.out.println(o);
    这又搞不懂了。
      

  8.   

    先说System.out.println((String)list.get(1)); 
    无论何时,将一个对象强转成String类型,都是不可取的。因为 1) 如果该对象本来就是一个String对象,那么强转就是画蛇添足了。2)如果该对象不是String对象,则强转一定会失败,因为String是final类,不能被继承。再来看System.out.println(list.get(1).toString());
    当编译器看到这行代码是,它要做的一项工作是确定list.get(1)的返回值类型,由于在前面有了形如 List <String> list=new ArrayList <String>() 的声明,编译器认为list.get(1)会返回一个String对象,因此这里调用的是String类中的toString()方法。而在运行时list.get(1)返回的实际上是一个Double对象,故而出错。
      

  9.   

    看一下源代码,明白了
    public E get(int index) {
        RangeCheck(index);
        return (E) elementData[index]; // 这里把Double直接转成String
    }相当于:
    Double d = 1.1;
    String str = (String) d; 
    当然是要报错的
      

  10.   

    楼上 ltandfyy 说得很对问题就在System.out.println(list.get(1)),但问题不是他说的构造函数问题
    1、当范型参数为String时,也就是程序中的List<String>时,System.out实际调用的是System.out.println(String)这个方法
    所以当list.add(1.1)后,取list.get(1)实际是一个Double,然后System.out.println(String)要把Double强制转换为String
    类型自然会抛出类型转化异常。
    2、那范型参数是Integer或者别的对象时,也就是List<Integer>时,为什么没有出现异常呢?
    这是因为System.out现在实际调用的是System.out.println(object)。要明白System.out.println(object)实际执行的是
    System.out.println(String.valueof(object));所以这个问题就是在println方法调用,跟范型实际没什么关系。
      

  11.   


    这位哥,为什么
    String s="1.1";
    Integer i=(Integer)s;
    不报错呢????
      

  12.   

    你上面的代码
     public static void go(List list) 
        { 
            list.add(1.1); 是double类型的
        } 
    下面的代码
     public static void go(List list) 
        { 
            list.add("1.1");  这个事string
        } 
    你的list定义式String的泛型
      

  13.   

    碰上这种问题只要看一下字节码
    第一种情况
     public static void main(java.lang.String[] args);
         0  new java.util.ArrayList [34]
         3  dup
         4  invokespecial java.util.ArrayList() [36]
         7  astore_1 [list]
         8  aload_1 [list]
         9  ldc <String "1"> [37]
        11  invokeinterface java.util.List.add(java.lang.Object) : boolean [24] [nargs: 2]
        16  pop
        17  aload_1 [list]
        18  invokestatic reflect.Test.go(java.util.List) : void [39]
        21  getstatic java.lang.System.out : java.io.PrintStream [41]
        24  aload_1 [list]
        25  iconst_1
        26  invokeinterface java.util.List.get(int) : java.lang.Object [47] [nargs: 2]
        31  checkcast java.lang.String [51]
        34  invokevirtual java.io.PrintStream.println(java.lang.String) : void [53]

        37  return
    第二种情况
      public static void main(java.lang.String[] args);
         0  new java.util.ArrayList [28]
         3  dup
         4  invokespecial java.util.ArrayList() [30]
         7  astore_1 [list]
         8  aload_1 [list]
         9  iconst_1
        10  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [31]
        13  invokeinterface java.util.List.add(java.lang.Object) : boolean [18] [nargs: 2]
        18  pop
        19  aload_1 [list]
        20  invokestatic reflect.Test1.go(java.util.List) : void [37]
        23  getstatic java.lang.System.out : java.io.PrintStream [39]
        26  aload_1 [list]
        27  iconst_1
        28  invokeinterface java.util.List.get(int) : java.lang.Object [45] [nargs: 2]
        33  invokevirtual java.io.PrintStream.println(java.lang.Object) : void [49]
        36  return第一种情况调用的是java.io.PrintStream.println(java.lang.String),而且在前面进行了一个String的类型检查checkcast java.lang.String [51]第二种情况调用的是 java.io.PrintStream.println(java.lang.Object),没做类型检查
      

  14.   

    至于编绎器为什么要这样做,为什么第二种情况不调用java.io.PrintStream.println(java.lang.Integer),第二种不做类型检查,看一下源代码就明白了println最终要调用一个print方法
    下面是参数为string的时候
     public void print(String s) {
     if (s == null) {
     s = "null";
     }
     write(s);
     } 
    当为基本类型时,print的实现方式
      public void print(int i) {
      write(String.valueOf(i));
     }   public void print(long l) {
     write(String.valueOf(l));
     }   public void print(float f) {
     write(String.valueOf(f));
     } ...... public void print(Object obj) {
     write(String.valueOf(obj));
     }
    我想大家应该已经看明白了,当是string的时候要判断一下,因此第一种范型为string时要做类型检查,而第二种编绎器将自作主张地调用了能用的print(Object obj)这个方法了,这样作有啥好处呢,因为少了一步装箱拆箱的工作
      

  15.   

    学习了。顺便说下:泛型只是用在编译阶段对编译的保证,数据是在运行时加入到List中的,编译阶段发现不了。Eclipse中,跟踪System.out.println()这个函数,即鼠标焦点在这个函数上后,按“F3”就可以看到它的原型了,
    和26楼看字节码达到同样的效果。前提是你要有jdk的原文件。
      

  16.   

    public static void go(List list) 
        { 
            list.add("1.1"); 
        } 
    这个方法里也要用泛型. 
    public static void go(List <Integer> list) 
        { 
            list.add(1.1); 
        } 
      

  17.   

    我觉得是编程习惯的问题,如果传值和参数确保是一致的情况下,就不会出现类似的错误。
    如:
    public static void go(List<String> list) //List<String>

            list.add("1.1"); 

    public static void main(String args[]) 
        { 
            List <String> list=new ArrayList <String>(); 
            list.add("1"); 
            go(list); 
            System.out.println(list.get(1)); 
      

  18.   

    public static void go(List list) 
        { 
            list.add("1.1"); 
        } 这个有什么错????