Java Generics casts strangely

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
10
down vote

favorite
5












I am using java 8.



I recently came across this:



public class Test 
public static void main(String args)
String ss = "" + (Test.<Integer>abc(2));
System.out.println(Test.<Integer>abc(2));

public static <T> T abc(T a)
String s = "adsa";
return (T) s;




This does not throw a java.lang.ClassCastException. Why is that?



I always thought + and System.out.println calls toString. But when I try to do that it throws an Exception as expected.



String sss = (Test.<Integer>abc(2)).toString();









share|improve this question







New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.















  • 5




    Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
    – VGR
    30 mins ago











  • Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
    – Oleksandr
    51 secs ago














up vote
10
down vote

favorite
5












I am using java 8.



I recently came across this:



public class Test 
public static void main(String args)
String ss = "" + (Test.<Integer>abc(2));
System.out.println(Test.<Integer>abc(2));

public static <T> T abc(T a)
String s = "adsa";
return (T) s;




This does not throw a java.lang.ClassCastException. Why is that?



I always thought + and System.out.println calls toString. But when I try to do that it throws an Exception as expected.



String sss = (Test.<Integer>abc(2)).toString();









share|improve this question







New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.















  • 5




    Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
    – VGR
    30 mins ago











  • Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
    – Oleksandr
    51 secs ago












up vote
10
down vote

favorite
5









up vote
10
down vote

favorite
5






5





I am using java 8.



I recently came across this:



public class Test 
public static void main(String args)
String ss = "" + (Test.<Integer>abc(2));
System.out.println(Test.<Integer>abc(2));

public static <T> T abc(T a)
String s = "adsa";
return (T) s;




This does not throw a java.lang.ClassCastException. Why is that?



I always thought + and System.out.println calls toString. But when I try to do that it throws an Exception as expected.



String sss = (Test.<Integer>abc(2)).toString();









share|improve this question







New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I am using java 8.



I recently came across this:



public class Test 
public static void main(String args)
String ss = "" + (Test.<Integer>abc(2));
System.out.println(Test.<Integer>abc(2));

public static <T> T abc(T a)
String s = "adsa";
return (T) s;




This does not throw a java.lang.ClassCastException. Why is that?



I always thought + and System.out.println calls toString. But when I try to do that it throws an Exception as expected.



String sss = (Test.<Integer>abc(2)).toString();






java generics casting






share|improve this question







New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question







New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question






New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 35 mins ago









zzrv

633




633




New contributor




zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






zzrv is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







  • 5




    Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
    – VGR
    30 mins ago











  • Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
    – Oleksandr
    51 secs ago












  • 5




    Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
    – VGR
    30 mins ago











  • Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
    – Oleksandr
    51 secs ago







5




5




Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
– VGR
30 mins ago





Enable all compiler warnings, and you will see the answer: your cast is not “safe.” Meaning, it is not doing what you think it’s doing. Specifically, generics are subject to type erasure, meaning they are only a means of enforcing safety at compile time, not runtime. At runtime, the method doesn’t know T is Integer. The compiler translated (T) to (Object) due to the declaration of <T>.
– VGR
30 mins ago













Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
– Oleksandr
51 secs ago




Possible duplicate of Why does this use of Generics not throw a runtime or compile time exception?
– Oleksandr
51 secs ago












2 Answers
2






active

oldest

votes

















up vote
4
down vote













It doesn't throw a ClassCastException because all generic type information is stripped from the compiled code (a process called type erasure). Basically, any type parameter is replaced by Object. That's why the first version works. It's also why the code compiles at all. If you ask the compiler to warn about unchecked or unsafe operations with the -Xlint:unchecked flag, you'll get a warning about an unchecked cast in the return statement of abc().



With this statement:



String sss = (Test.<Integer>abc(2)).toString();


the story is a bit different. While the type parameter T is replaced by Object, the calling code gets translated into byte code that explicitly casts the result to Integer. It is as if the code were written with a method with signature static Object abc(Object) and the statement were written:



String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();


That is, not only does the cast inside abc() go away due to type erasure, a new cast is inserted by the compiler in the calling code. This cast generates a ClassCastException at run time because the object returned from abc() is a String, not an Integer.



Note that the statement



String ss = "" + (Test.<Integer>abc(2));


doesn't require a cast because the compiler translates it into something that might have been written:



String ss = "" + String.valueOf(Test.<Integer>abc(2));


The compiler is smart enough to recognize that no cast is needed for this code.






share|improve this answer





























    up vote
    3
    down vote













    Generics disappear at runtime, so a cast to T is really just a cast to Object (which the compiler will actually just get rid of), so there's no class cast exception.



    abc is just a method which takes an object and returns an object. StringBuilder.append(Object) is the method which is called, as can be seen from the bytecode:



    ... 
    16: invokestatic #7 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
    19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
    ...


    When you do



    String sss = (Test.<Integer>abc(2)).toString();


    Then the bytecode is



    ...
    4: invokestatic #3 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
    7: checkcast #4 // class java/lang/Integer
    10: invokevirtual #5 // Method java/lang/Integer.toString:()Ljava/lang/String;
    ...


    Your code fails at the checkcast operation, which was not present before.






    share|improve this answer






















      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );






      zzrv is a new contributor. Be nice, and check out our Code of Conduct.









       

      draft saved


      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53175130%2fjava-generics-casts-strangely%23new-answer', 'question_page');

      );

      Post as a guest






























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      4
      down vote













      It doesn't throw a ClassCastException because all generic type information is stripped from the compiled code (a process called type erasure). Basically, any type parameter is replaced by Object. That's why the first version works. It's also why the code compiles at all. If you ask the compiler to warn about unchecked or unsafe operations with the -Xlint:unchecked flag, you'll get a warning about an unchecked cast in the return statement of abc().



      With this statement:



      String sss = (Test.<Integer>abc(2)).toString();


      the story is a bit different. While the type parameter T is replaced by Object, the calling code gets translated into byte code that explicitly casts the result to Integer. It is as if the code were written with a method with signature static Object abc(Object) and the statement were written:



      String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();


      That is, not only does the cast inside abc() go away due to type erasure, a new cast is inserted by the compiler in the calling code. This cast generates a ClassCastException at run time because the object returned from abc() is a String, not an Integer.



      Note that the statement



      String ss = "" + (Test.<Integer>abc(2));


      doesn't require a cast because the compiler translates it into something that might have been written:



      String ss = "" + String.valueOf(Test.<Integer>abc(2));


      The compiler is smart enough to recognize that no cast is needed for this code.






      share|improve this answer


























        up vote
        4
        down vote













        It doesn't throw a ClassCastException because all generic type information is stripped from the compiled code (a process called type erasure). Basically, any type parameter is replaced by Object. That's why the first version works. It's also why the code compiles at all. If you ask the compiler to warn about unchecked or unsafe operations with the -Xlint:unchecked flag, you'll get a warning about an unchecked cast in the return statement of abc().



        With this statement:



        String sss = (Test.<Integer>abc(2)).toString();


        the story is a bit different. While the type parameter T is replaced by Object, the calling code gets translated into byte code that explicitly casts the result to Integer. It is as if the code were written with a method with signature static Object abc(Object) and the statement were written:



        String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();


        That is, not only does the cast inside abc() go away due to type erasure, a new cast is inserted by the compiler in the calling code. This cast generates a ClassCastException at run time because the object returned from abc() is a String, not an Integer.



        Note that the statement



        String ss = "" + (Test.<Integer>abc(2));


        doesn't require a cast because the compiler translates it into something that might have been written:



        String ss = "" + String.valueOf(Test.<Integer>abc(2));


        The compiler is smart enough to recognize that no cast is needed for this code.






        share|improve this answer
























          up vote
          4
          down vote










          up vote
          4
          down vote









          It doesn't throw a ClassCastException because all generic type information is stripped from the compiled code (a process called type erasure). Basically, any type parameter is replaced by Object. That's why the first version works. It's also why the code compiles at all. If you ask the compiler to warn about unchecked or unsafe operations with the -Xlint:unchecked flag, you'll get a warning about an unchecked cast in the return statement of abc().



          With this statement:



          String sss = (Test.<Integer>abc(2)).toString();


          the story is a bit different. While the type parameter T is replaced by Object, the calling code gets translated into byte code that explicitly casts the result to Integer. It is as if the code were written with a method with signature static Object abc(Object) and the statement were written:



          String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();


          That is, not only does the cast inside abc() go away due to type erasure, a new cast is inserted by the compiler in the calling code. This cast generates a ClassCastException at run time because the object returned from abc() is a String, not an Integer.



          Note that the statement



          String ss = "" + (Test.<Integer>abc(2));


          doesn't require a cast because the compiler translates it into something that might have been written:



          String ss = "" + String.valueOf(Test.<Integer>abc(2));


          The compiler is smart enough to recognize that no cast is needed for this code.






          share|improve this answer














          It doesn't throw a ClassCastException because all generic type information is stripped from the compiled code (a process called type erasure). Basically, any type parameter is replaced by Object. That's why the first version works. It's also why the code compiles at all. If you ask the compiler to warn about unchecked or unsafe operations with the -Xlint:unchecked flag, you'll get a warning about an unchecked cast in the return statement of abc().



          With this statement:



          String sss = (Test.<Integer>abc(2)).toString();


          the story is a bit different. While the type parameter T is replaced by Object, the calling code gets translated into byte code that explicitly casts the result to Integer. It is as if the code were written with a method with signature static Object abc(Object) and the statement were written:



          String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();


          That is, not only does the cast inside abc() go away due to type erasure, a new cast is inserted by the compiler in the calling code. This cast generates a ClassCastException at run time because the object returned from abc() is a String, not an Integer.



          Note that the statement



          String ss = "" + (Test.<Integer>abc(2));


          doesn't require a cast because the compiler translates it into something that might have been written:



          String ss = "" + String.valueOf(Test.<Integer>abc(2));


          The compiler is smart enough to recognize that no cast is needed for this code.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 13 mins ago

























          answered 24 mins ago









          Ted Hopp

          197k40307416




          197k40307416






















              up vote
              3
              down vote













              Generics disappear at runtime, so a cast to T is really just a cast to Object (which the compiler will actually just get rid of), so there's no class cast exception.



              abc is just a method which takes an object and returns an object. StringBuilder.append(Object) is the method which is called, as can be seen from the bytecode:



              ... 
              16: invokestatic #7 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
              19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
              ...


              When you do



              String sss = (Test.<Integer>abc(2)).toString();


              Then the bytecode is



              ...
              4: invokestatic #3 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
              7: checkcast #4 // class java/lang/Integer
              10: invokevirtual #5 // Method java/lang/Integer.toString:()Ljava/lang/String;
              ...


              Your code fails at the checkcast operation, which was not present before.






              share|improve this answer


























                up vote
                3
                down vote













                Generics disappear at runtime, so a cast to T is really just a cast to Object (which the compiler will actually just get rid of), so there's no class cast exception.



                abc is just a method which takes an object and returns an object. StringBuilder.append(Object) is the method which is called, as can be seen from the bytecode:



                ... 
                16: invokestatic #7 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
                ...


                When you do



                String sss = (Test.<Integer>abc(2)).toString();


                Then the bytecode is



                ...
                4: invokestatic #3 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                7: checkcast #4 // class java/lang/Integer
                10: invokevirtual #5 // Method java/lang/Integer.toString:()Ljava/lang/String;
                ...


                Your code fails at the checkcast operation, which was not present before.






                share|improve this answer
























                  up vote
                  3
                  down vote










                  up vote
                  3
                  down vote









                  Generics disappear at runtime, so a cast to T is really just a cast to Object (which the compiler will actually just get rid of), so there's no class cast exception.



                  abc is just a method which takes an object and returns an object. StringBuilder.append(Object) is the method which is called, as can be seen from the bytecode:



                  ... 
                  16: invokestatic #7 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                  19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
                  ...


                  When you do



                  String sss = (Test.<Integer>abc(2)).toString();


                  Then the bytecode is



                  ...
                  4: invokestatic #3 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                  7: checkcast #4 // class java/lang/Integer
                  10: invokevirtual #5 // Method java/lang/Integer.toString:()Ljava/lang/String;
                  ...


                  Your code fails at the checkcast operation, which was not present before.






                  share|improve this answer














                  Generics disappear at runtime, so a cast to T is really just a cast to Object (which the compiler will actually just get rid of), so there's no class cast exception.



                  abc is just a method which takes an object and returns an object. StringBuilder.append(Object) is the method which is called, as can be seen from the bytecode:



                  ... 
                  16: invokestatic #7 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                  19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
                  ...


                  When you do



                  String sss = (Test.<Integer>abc(2)).toString();


                  Then the bytecode is



                  ...
                  4: invokestatic #3 // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
                  7: checkcast #4 // class java/lang/Integer
                  10: invokevirtual #5 // Method java/lang/Integer.toString:()Ljava/lang/String;
                  ...


                  Your code fails at the checkcast operation, which was not present before.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 4 mins ago

























                  answered 21 mins ago









                  Michael

                  17.4k73066




                  17.4k73066




















                      zzrv is a new contributor. Be nice, and check out our Code of Conduct.









                       

                      draft saved


                      draft discarded


















                      zzrv is a new contributor. Be nice, and check out our Code of Conduct.












                      zzrv is a new contributor. Be nice, and check out our Code of Conduct.











                      zzrv is a new contributor. Be nice, and check out our Code of Conduct.













                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53175130%2fjava-generics-casts-strangely%23new-answer', 'question_page');

                      );

                      Post as a guest













































































                      Comments

                      Popular posts from this blog

                      What does second last employer means? [closed]

                      List of Gilmore Girls characters

                      Confectionery