RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 721089
Accepted
Direct
Direct
Asked:2020-09-21 04:55:12 +0000 UTC2020-09-21 04:55:12 +0000 UTC 2020-09-21 04:55:12 +0000 UTC

使用泛型时,将数字转换为字符串不会引发 ClassCastExeption - 为什么?

  • 772

你能解释一下为什么//(?!)没有在行中抛出 ClassCastExeption 异常吗?There (T)is , where T=String, 这意味着 there (String) new Integer(42), 应该抛出异常...... JVM 应该转换类型,但由于某种原因它没有。然后我们看看类型t,看看Integer什么是真正的类型t。但是我们推断类型的事实表明没有抛出异常。然后程序在调用函数的地方抛出异常A.<String>f()。

顺便一提!我记得 Bruce Eckel 教过有一些 javap 反编译器,他自己使用字节码或 .class 编写程序代码……我没有研究它。如果有人精通这一点,请尝试让反编译器构建这段代码,也许它会显示那里真正发生了什么,为什么?

public class A {
    public static <T> T f() {
        T t = (T) new Integer(42); // (?!) ЧТО ЧЁРТ ВОЗЬМИ ЗДЕСЬ ВООБЩЕ ПРОИСХОДИТ?!
        System.out.println(t.getClass());
        return t;
    }

    public static void main(String[] args) {
        System.out.println(A.<String>f()); // как здесь возможно исключение? ахаха, вы серьёзно?
    }
}

新:
所以,朋友们。最常见的答案是,方法f()是已编译为常规方法的常规方法,但考虑到其调用结果被强制转换为传递为的类型这一事实<NameOfTheClass>。一切似乎都已经很好了,这个答案可以被接受,但如果它在 Java 中真的像这样工作......关键是你可以main(String[])尝试调用更具爆炸性的东西:

System.out.println(A.<Double>f());

令人惊讶的是,控制台会输出:

//output:
class java.lang.Integer
42

所以如果我们让它不可见,它看起来像这样:

System.out.println((Double)A.<Double>f()); 

然后尝试编译它 - 会出现错误啊哈哈哈,从那里得出的结论是它不能完全一样......

我对此的猜测是该方法println()被重载,并且在A.<String>f()它选择版本println(String arg)的情况下,并且在A.<Double>f()它理解Double没有版本并设置println(Object arg)它的情况下,因为对于我们的, 42 被(Object)Integer(42)调用.toString()并显示。

对我来说,奇怪的是,由于某种原因,在第一种情况下,当我们调用A.<String>f()它时,这里的许多人认为它是这样调用(String)A.<String>f()的,从它设置版本的位置开始println(String arg),而第二次调用时A.<Double>f(),它确实如此没有尝试这样做(Double)A.<Double>f(),它立即选择了println(Object arg)...。

您想说整个技术非常智能,如果println()成功检查参数化/泛型函数的返回是否作为重载方法中的参数存在,则不会调用强制转换[例如,String版本println()存在,是的,让我们将输出转换Object为A.f()指定的String],如果重载版本没有在类的类型参数中指定版本A.f(),println()那么它(编译器)离开版本println(Object)并决定不尝试(Object)(Double)A.<Double>f()做是结果f()本身Object吗?

总计:我想要解释参数化方法(类)是如何工作的,这样的解释是编译器对生成代码的操作的算法是清楚的。如果我对编译器操作的一些猜测是正确的,那么我希望他们(我的猜测)得到确认,并且非常希望(!)带有指向一些文档文件的链接!

java
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Qwertiy
    2020-09-21T07:18:14Z2020-09-21T07:18:14Z

    运行时不存在泛型类型。这些只是编译时检查。

    public static <T> T f() {
    
    public static Object f() {
    
    T t = (T) new Integer(42); // (?!) ЧТО ЧЁРТ ВОЗЬМИ ЗДЕСЬ ВООБЩЕ ПРОИСХОДИТ?!
    
    Object t = (Object) new Integer(42); // всё хорошо
    
    return t;
    

    一切都还好。目的。

    System.out.println(A.<String>f()); // как здесь возможно исключение? ахаха, вы серьёзно?
    
    System.out.println((String)f());
    

    哎呀..没有字符串。这是例外。


    结果是这样的:

    (String) (Object) new Integer(42);
             ^^^^^^^^------------------ Integer - наследник Object
    ^^^^^^^^--------------------------- String - наследник Object
    

    编译过程中没有错误,但是执行时,第一个转换结果是 inf并且它是有效的,但是第二个转换已经 in main,它落在哪里。

    • 14
  2. Best Answer
    iksuy
    2020-09-21T17:30:45Z2020-09-21T17:30:45Z

    这是main调用时的字节码A.<Double>f()

    public static void main(java.lang.String[]);
        Code:
           0: getstatic     #4    // Field java/lang/System.out:Ljava/io/PrintStream;
           3: invokestatic  #7    // Method f:()Ljava/lang/Object;
           6: invokevirtual #6    // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
           9: return
    

    这是main调用时的字节码A<String>.f()

    public static void main(java.lang.String[]);
        Code:
           0: getstatic     #4      // Field java/lang/System.out:Ljava/io/PrintStream;
           3: invokestatic  #7      // Method f:()Ljava/lang/Object;
           6: checkcast     #8      // class java/lang/String
           9: invokevirtual #9      // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          12: return
    

    注意这行invokevirtual,第一种情况,调用带有签名的方法,println(Object o)第二种println(String s) 情况-擦除Double类型后,方法返回,没有方法f(),Object里面平静println调用,一切正常美好的。在有一个方法的情况下,编译器决定需要调用它,所以那里有一个强制转换。DoubleDoubleprintlndoubleObjectString.valueOfStringprintlnString

    我包裹System.out.println在我println的 3 种类型:

    static void println(Double d){
        System.out.println(String.valueOf(d));
    }
    
    static void println(String s){
        System.out.println(String.valueOf(s));
    }
    
    static void println(Object o){
        System.out.println(String.valueOf(o));
    }
    

    而现在你已经猜到了当你打电话时会发生什么?

    public static void main(String[] args) {
        println(Main.<Double>f()); 
    }
    

    编译器将看到有一个重载println的 forDouble并将强制转换(这就是错误所在):

    public static void main(java.lang.String[]);
        Code:
           0: invokestatic  #7     // Method f:()Ljava/lang/Object;
           3: checkcast     #8     // class java/lang/Double
           6: invokestatic  #9     // Method println:(Ljava/lang/Double;)V
           9: return
    
    • 10
  3. Artem Konovalov
    2020-09-21T05:35:02Z2020-09-21T05:35:02Z

    如果有不清楚的地方,您可以随时查看bytecode:

    public class ru.izebit.A {
      public ru.izebit.A();
         Code:
          0: aload_0
          1: invokespecial  #1  // Method java/lang/Object."<init>":()V
          4: return
    
      public static <T> T f();
         Code:
          0: new            #2  // class java/lang/Integer
          3: dup
          4: bipush  42
          6: invokespecial  #3 // Method java/lang/Integer."<init>":(I)V
          9: astore_0
          10: getstatic     #4 // Field java/lang/System.out:Ljava/io/PrintStream;
          13: aload_0
          14: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class;
          17: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          20: aload_0
          21: areturn
    
    public static void main(java.lang.String[]);
      Code:
           0: getstatic     #4  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: invokestatic  #7  // Method f:()Ljava/lang/Object;
           6: checkcast     #8  // class java/lang/String
           9: invokevirtual #9  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           12: return
    }
    

    从代码中可以看出,该方法f()并没有强制转换为String(显然,新对象所分配的引用类型将是Object)。但是在从参数化方法返回此对象的那一刻,发生了相同的强制转换,这就是发生异常的原因。

    • 7
  4. ЮрийСПб
    2020-09-21T06:22:58Z2020-09-21T06:22:58Z

    泛型类型只能是非原始类型,即 最简单泛型的超类型是Object. 这样

    //позволяет любые непримитивы
    public class MyClass<T> {
        T t;
    
        public MyClass(T tArg) {
           t = tArg;
        }
    }
    

    上面的类只允许在自身内部调用变量上的t类方法Object。那些。t类中所有代码的变量都是MyClass类型Object。ClassCastException这解释了将数字转换为对象时的缺失。

    但是如果我们更精确地指定泛型的类型,那么我们会得到一个编译错误(并且将无法到达运行时错误):

    //можно использовать только String или его предков
    public class MyClass<? super String> {
        T t;
    
        public MyClass(T tArg) {
           t = new Integer(42); //ОШИБКА компиляции - переменной t можно присваивать String или Object
        }
    }
    

    左右,然而,这是没有意义的,因为。String- 最后一类(即“不育” - 它不能扩展,也不能有后代)

    //можно использовать только String и его потомков
    public class MyClass<T extends String> {
        T t;
    
        public MyClass(T tArg) {
           t = new Integer(42); //ОШИБКА компиляции - переменной t можно присваивать String или его наследников
        }
    }
    

    类型推断的正确操作有不同的解释:

    public static <T> T f() {
        T t = (T) new Integer(42); //Всё ОК, т.к. внутри метода T - всегда Object
        System.out.println(t.getClass()); //выведет конкретный тип (String, Integer etc)
    }
    

    方法是Java虚拟的。那些。不会调用类方法T(即Object在方法内),而是调用内存中特定类实现的方法Object。这已经是String,Integer等等。


    最终的运行时错误显然与码头中的示例相似,并且可以类似地重写代码,如下所示:

     System.out.println((String) A.<String>f());
    

    那些。你已经指出该方法应该返回一个字符串,调用该方法System.out.println(String arg),将一个类型引用传递给它Object,它实际上存储了一个类型的对象Integer。并且在将存储在内存中的特定数字转换为字符串时,会发生异常

    • 6

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5