How does casting this object to a generic type work?





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







10















My understanding is generic types are invariant, so if we have B as a subtype of A, then List<B> has no relationship with List<A>. So casting won't work on List<A> and List<B>.



From Effective Java Third Edition we have this snippet of code:



// Generic singleton factory pattern
private static UnaryOperator<Object> IDENTIFY_FN = (t) -> t;

@SuppressWarnings("unchecked")
public static <T> UnaryOperator<T> identifyFunction() {
return (UnaryOperator<T>) IDENTIFY_FN; //OK But how, why?
}

public static void main(String args) {
String strings = {"a", "b", "c"};
UnaryOperator<String> sameString = identifyFunction();
for (String s : strings) {
System.out.println(sameString.apply(s));
}
}


Here I am confused. We have cast IDENTIFY_FN, whose type is UnaryOperator<Object>, to UnaryOperator<T>, which has another type parameter.



When type erasure happens String is a subtype of Object, but so far as I know UnaryOperator<String> is not a subtype of UnaryOperator<Object>.



Do Object and T relate somehow? And how does casting succeed in this case?










share|improve this question































    10















    My understanding is generic types are invariant, so if we have B as a subtype of A, then List<B> has no relationship with List<A>. So casting won't work on List<A> and List<B>.



    From Effective Java Third Edition we have this snippet of code:



    // Generic singleton factory pattern
    private static UnaryOperator<Object> IDENTIFY_FN = (t) -> t;

    @SuppressWarnings("unchecked")
    public static <T> UnaryOperator<T> identifyFunction() {
    return (UnaryOperator<T>) IDENTIFY_FN; //OK But how, why?
    }

    public static void main(String args) {
    String strings = {"a", "b", "c"};
    UnaryOperator<String> sameString = identifyFunction();
    for (String s : strings) {
    System.out.println(sameString.apply(s));
    }
    }


    Here I am confused. We have cast IDENTIFY_FN, whose type is UnaryOperator<Object>, to UnaryOperator<T>, which has another type parameter.



    When type erasure happens String is a subtype of Object, but so far as I know UnaryOperator<String> is not a subtype of UnaryOperator<Object>.



    Do Object and T relate somehow? And how does casting succeed in this case?










    share|improve this question



























      10












      10








      10








      My understanding is generic types are invariant, so if we have B as a subtype of A, then List<B> has no relationship with List<A>. So casting won't work on List<A> and List<B>.



      From Effective Java Third Edition we have this snippet of code:



      // Generic singleton factory pattern
      private static UnaryOperator<Object> IDENTIFY_FN = (t) -> t;

      @SuppressWarnings("unchecked")
      public static <T> UnaryOperator<T> identifyFunction() {
      return (UnaryOperator<T>) IDENTIFY_FN; //OK But how, why?
      }

      public static void main(String args) {
      String strings = {"a", "b", "c"};
      UnaryOperator<String> sameString = identifyFunction();
      for (String s : strings) {
      System.out.println(sameString.apply(s));
      }
      }


      Here I am confused. We have cast IDENTIFY_FN, whose type is UnaryOperator<Object>, to UnaryOperator<T>, which has another type parameter.



      When type erasure happens String is a subtype of Object, but so far as I know UnaryOperator<String> is not a subtype of UnaryOperator<Object>.



      Do Object and T relate somehow? And how does casting succeed in this case?










      share|improve this question
















      My understanding is generic types are invariant, so if we have B as a subtype of A, then List<B> has no relationship with List<A>. So casting won't work on List<A> and List<B>.



      From Effective Java Third Edition we have this snippet of code:



      // Generic singleton factory pattern
      private static UnaryOperator<Object> IDENTIFY_FN = (t) -> t;

      @SuppressWarnings("unchecked")
      public static <T> UnaryOperator<T> identifyFunction() {
      return (UnaryOperator<T>) IDENTIFY_FN; //OK But how, why?
      }

      public static void main(String args) {
      String strings = {"a", "b", "c"};
      UnaryOperator<String> sameString = identifyFunction();
      for (String s : strings) {
      System.out.println(sameString.apply(s));
      }
      }


      Here I am confused. We have cast IDENTIFY_FN, whose type is UnaryOperator<Object>, to UnaryOperator<T>, which has another type parameter.



      When type erasure happens String is a subtype of Object, but so far as I know UnaryOperator<String> is not a subtype of UnaryOperator<Object>.



      Do Object and T relate somehow? And how does casting succeed in this case?







      java generics casting effective-java






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 17 '18 at 3:17









      Boann

      37.4k1290122




      37.4k1290122










      asked Nov 16 '18 at 12:00









      hamza belmelloukihamza belmellouki

      171111




      171111
























          4 Answers
          4






          active

          oldest

          votes


















          2














          Generics don't exist at runtime. At runtime, every UnaryOperator<T> is a UnaryOperator<Object>. The cast is necessary to placate the compiler at compile-time. At runtime it's meaningless.






          share|improve this answer



















          • 3





            The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

            – OhleC
            Nov 16 '18 at 12:19






          • 1





            @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

            – Michael
            Nov 16 '18 at 12:21








          • 1





            @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

            – Michael
            Nov 16 '18 at 12:28






          • 1





            I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

            – OhleC
            Nov 16 '18 at 12:34











          • Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

            – user10639668
            Nov 16 '18 at 12:40





















          4














          This cast compiles, because it's a special case of a narrowing conversion. (According to §5.5, narrowing conversions are one of the types of conversions allowed by a cast, so most of this answer is going to focus on the rules for narrowing conversions.)



          Note that while UnaryOperator<T> is not a subtype of UnaryOperator<Object> (so the cast isn't a "downcast"), it's still considered a narrowing conversion. From §5.6.1:




          A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. [...] Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.




          Some of these "sideways" casts fail due to special rules, for example the following will fail:



          List<String> a = ...;
          List<Double> b = (List<String>) a;


          Specifically, this is given by a rule in §5.1.6.1 which states that:






          • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).



            Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.






          In other words, if a and b have a common supertype with the same erasure (in this case, for example, List), then they must be what the JLS is calling "provably distinct", given by §4.5:




          Two parameterized types are provably distinct if either of the following is true:




          • They are parameterizations of distinct generic type declarations.


          • Any of their type arguments are provably distinct.





          And §4.5.1:




          Two type arguments are provably distinct if one of the following is true:




          • Neither argument is a type variable or wildcard, and the two arguments are not the same type.


          • One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S|.


          • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.





          So, given the above rules, List<String> and List<Double> are provably distinct (via the 1st rule from 4.5.1), because String and Double are different type arguments.



          However, UnaryOperator<T> and UnaryOperator<Object> are not provably distinct (via the 2nd rule from 4.5.1), because:




          1. One type argument is a type variable (T, with a bound of Object.)


          2. The bound of that type variable is the same as the type argument to the other type (Object).



          Since UnaryOperator<T> and UnaryOperator<Object> are not provably distinct, the narrowing conversion is allowed, hence the cast compiles.





          One way to think about why the compiler permits some of these casts but not others is: in the case of the type variable, it can't prove that T definitely isn't Object. For example, we could have a situation like this:



          UnaryOperator<String> aStringThing = Somewhere::doStringThing;
          UnaryOperator<Double> aDoubleThing = Somewhere::doDoubleThing;

          <T> UnaryOperator<T> getThing(Class<T> t) {
          if (t == String.class)
          return (UnaryOperator<T>) aStringThing;
          if (t == Double.class)
          return (UnaryOperator<T>) aDoubleThing;
          return null;
          }


          In those cases, we actually know the cast is correct as long as nobody else is doing something funny (like unchecked casting the Class<T> argument).



          So in the general case of casting to UnaryOperator<T>, we might actually be doing something legitimate. In comparison, with the case of casting List<String> to List<Double>, we can say pretty authoritatively that it's always wrong.






          share|improve this answer

































            3














            The JLS allows such cast:




            A cast from a type S to a parameterized type T is unchecked
            unless at least one of the following conditions holds:




            • S <: T


            • All of the type arguments of T are unbounded wildcards.


            • [ ... ]





            As a result, an unchecked cast causes a compile-time unchecked warning, unless suppressed by the SuppressWarnings annotation.



            Furthermore, during the type erasure process, identifyFunction and IDENTIFY_FN compiles into:



            private static UnaryOperator IDENTIFY_FN;

            public static UnaryOperator identifyFunction() {
            return IDENTIFY_FN; // cast is removed
            }


            and the checkcast is added to the call site:



            System.out.println(sameString.apply(s));
            ^
            INVOKEINTERFACE java/util/function/UnaryOperator.apply (Ljava/lang/Object)Ljava/lang/Object
            CHECKCAST java/lang/String
            INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V


            checkcast succeeds, because the identity function returns its argument unmodified.






            share|improve this answer

































              1














              The cast



              return (UnaryOperator<T>) IDENTIFY_FN;


              basically amounts to a cast to the raw type UnaryOperator, because T is erased at runtime and ignored for the purpose of casts at compile-time. You can cast a generic type to its raw type (for backwards compatibility reasons), but you should get an "unchecked" warning.



              This would also work, for example:



              UnaryOperator<String> foo = (UnaryOperator) IDENTITY_FN;





              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',
                autoActivateHeartbeat: false,
                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
                });


                }
                });














                draft saved

                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53337494%2fhow-does-casting-this-object-to-a-generic-type-work%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                4 Answers
                4






                active

                oldest

                votes








                4 Answers
                4






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                2














                Generics don't exist at runtime. At runtime, every UnaryOperator<T> is a UnaryOperator<Object>. The cast is necessary to placate the compiler at compile-time. At runtime it's meaningless.






                share|improve this answer



















                • 3





                  The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                  – OhleC
                  Nov 16 '18 at 12:19






                • 1





                  @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                  – Michael
                  Nov 16 '18 at 12:21








                • 1





                  @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                  – Michael
                  Nov 16 '18 at 12:28






                • 1





                  I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                  – OhleC
                  Nov 16 '18 at 12:34











                • Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                  – user10639668
                  Nov 16 '18 at 12:40


















                2














                Generics don't exist at runtime. At runtime, every UnaryOperator<T> is a UnaryOperator<Object>. The cast is necessary to placate the compiler at compile-time. At runtime it's meaningless.






                share|improve this answer



















                • 3





                  The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                  – OhleC
                  Nov 16 '18 at 12:19






                • 1





                  @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                  – Michael
                  Nov 16 '18 at 12:21








                • 1





                  @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                  – Michael
                  Nov 16 '18 at 12:28






                • 1





                  I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                  – OhleC
                  Nov 16 '18 at 12:34











                • Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                  – user10639668
                  Nov 16 '18 at 12:40
















                2












                2








                2







                Generics don't exist at runtime. At runtime, every UnaryOperator<T> is a UnaryOperator<Object>. The cast is necessary to placate the compiler at compile-time. At runtime it's meaningless.






                share|improve this answer













                Generics don't exist at runtime. At runtime, every UnaryOperator<T> is a UnaryOperator<Object>. The cast is necessary to placate the compiler at compile-time. At runtime it's meaningless.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 16 '18 at 12:13









                MichaelMichael

                21.5k83573




                21.5k83573








                • 3





                  The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                  – OhleC
                  Nov 16 '18 at 12:19






                • 1





                  @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                  – Michael
                  Nov 16 '18 at 12:21








                • 1





                  @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                  – Michael
                  Nov 16 '18 at 12:28






                • 1





                  I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                  – OhleC
                  Nov 16 '18 at 12:34











                • Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                  – user10639668
                  Nov 16 '18 at 12:40
















                • 3





                  The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                  – OhleC
                  Nov 16 '18 at 12:19






                • 1





                  @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                  – Michael
                  Nov 16 '18 at 12:21








                • 1





                  @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                  – Michael
                  Nov 16 '18 at 12:28






                • 1





                  I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                  – OhleC
                  Nov 16 '18 at 12:34











                • Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                  – user10639668
                  Nov 16 '18 at 12:40










                3




                3





                The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                – OhleC
                Nov 16 '18 at 12:19





                The question was why the cast is legal at compile-time. A direct cast like (UnityOperator<String>) IDENTITY_FN would not be legal.

                – OhleC
                Nov 16 '18 at 12:19




                1




                1





                @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                – Michael
                Nov 16 '18 at 12:21







                @ohlec He already answered that. Generics are invariant. UnityOperator<String> != UnityOperator<Object>. As I said, UnaryOperator<T> is UnaryOperator<Object>.

                – Michael
                Nov 16 '18 at 12:21






                1




                1





                @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                – Michael
                Nov 16 '18 at 12:28





                @ohlec What's your point? Type bounds are compile-time checks. Casts are runtime operations. A compiler will stop you doing a cast that it knows cannot succeed (useful) but otherwise will not step in.

                – Michael
                Nov 16 '18 at 12:28




                1




                1





                I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                – OhleC
                Nov 16 '18 at 12:34





                I think this is exactly what the asker was unsure about. The compiler complains when it knows a cast will fail, but not when it knows a cast might fail, like in this case.

                – OhleC
                Nov 16 '18 at 12:34













                Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                – user10639668
                Nov 16 '18 at 12:40







                Even though I upvoted your answer, I have problems explaining why the compiler does not complains about this: private static List<Object> IDENTIFY_FN = new ArrayList<Object>(); @SuppressWarnings("unchecked") public static <T> UnaryOperator<T> identifyFunction() { return (UnaryOperator<T>) IDENTIFY_FN; }

                – user10639668
                Nov 16 '18 at 12:40















                4














                This cast compiles, because it's a special case of a narrowing conversion. (According to §5.5, narrowing conversions are one of the types of conversions allowed by a cast, so most of this answer is going to focus on the rules for narrowing conversions.)



                Note that while UnaryOperator<T> is not a subtype of UnaryOperator<Object> (so the cast isn't a "downcast"), it's still considered a narrowing conversion. From §5.6.1:




                A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. [...] Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.




                Some of these "sideways" casts fail due to special rules, for example the following will fail:



                List<String> a = ...;
                List<Double> b = (List<String>) a;


                Specifically, this is given by a rule in §5.1.6.1 which states that:






                • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).



                  Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.






                In other words, if a and b have a common supertype with the same erasure (in this case, for example, List), then they must be what the JLS is calling "provably distinct", given by §4.5:




                Two parameterized types are provably distinct if either of the following is true:




                • They are parameterizations of distinct generic type declarations.


                • Any of their type arguments are provably distinct.





                And §4.5.1:




                Two type arguments are provably distinct if one of the following is true:




                • Neither argument is a type variable or wildcard, and the two arguments are not the same type.


                • One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S|.


                • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.





                So, given the above rules, List<String> and List<Double> are provably distinct (via the 1st rule from 4.5.1), because String and Double are different type arguments.



                However, UnaryOperator<T> and UnaryOperator<Object> are not provably distinct (via the 2nd rule from 4.5.1), because:




                1. One type argument is a type variable (T, with a bound of Object.)


                2. The bound of that type variable is the same as the type argument to the other type (Object).



                Since UnaryOperator<T> and UnaryOperator<Object> are not provably distinct, the narrowing conversion is allowed, hence the cast compiles.





                One way to think about why the compiler permits some of these casts but not others is: in the case of the type variable, it can't prove that T definitely isn't Object. For example, we could have a situation like this:



                UnaryOperator<String> aStringThing = Somewhere::doStringThing;
                UnaryOperator<Double> aDoubleThing = Somewhere::doDoubleThing;

                <T> UnaryOperator<T> getThing(Class<T> t) {
                if (t == String.class)
                return (UnaryOperator<T>) aStringThing;
                if (t == Double.class)
                return (UnaryOperator<T>) aDoubleThing;
                return null;
                }


                In those cases, we actually know the cast is correct as long as nobody else is doing something funny (like unchecked casting the Class<T> argument).



                So in the general case of casting to UnaryOperator<T>, we might actually be doing something legitimate. In comparison, with the case of casting List<String> to List<Double>, we can say pretty authoritatively that it's always wrong.






                share|improve this answer






























                  4














                  This cast compiles, because it's a special case of a narrowing conversion. (According to §5.5, narrowing conversions are one of the types of conversions allowed by a cast, so most of this answer is going to focus on the rules for narrowing conversions.)



                  Note that while UnaryOperator<T> is not a subtype of UnaryOperator<Object> (so the cast isn't a "downcast"), it's still considered a narrowing conversion. From §5.6.1:




                  A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. [...] Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.




                  Some of these "sideways" casts fail due to special rules, for example the following will fail:



                  List<String> a = ...;
                  List<Double> b = (List<String>) a;


                  Specifically, this is given by a rule in §5.1.6.1 which states that:






                  • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).



                    Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.






                  In other words, if a and b have a common supertype with the same erasure (in this case, for example, List), then they must be what the JLS is calling "provably distinct", given by §4.5:




                  Two parameterized types are provably distinct if either of the following is true:




                  • They are parameterizations of distinct generic type declarations.


                  • Any of their type arguments are provably distinct.





                  And §4.5.1:




                  Two type arguments are provably distinct if one of the following is true:




                  • Neither argument is a type variable or wildcard, and the two arguments are not the same type.


                  • One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S|.


                  • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.





                  So, given the above rules, List<String> and List<Double> are provably distinct (via the 1st rule from 4.5.1), because String and Double are different type arguments.



                  However, UnaryOperator<T> and UnaryOperator<Object> are not provably distinct (via the 2nd rule from 4.5.1), because:




                  1. One type argument is a type variable (T, with a bound of Object.)


                  2. The bound of that type variable is the same as the type argument to the other type (Object).



                  Since UnaryOperator<T> and UnaryOperator<Object> are not provably distinct, the narrowing conversion is allowed, hence the cast compiles.





                  One way to think about why the compiler permits some of these casts but not others is: in the case of the type variable, it can't prove that T definitely isn't Object. For example, we could have a situation like this:



                  UnaryOperator<String> aStringThing = Somewhere::doStringThing;
                  UnaryOperator<Double> aDoubleThing = Somewhere::doDoubleThing;

                  <T> UnaryOperator<T> getThing(Class<T> t) {
                  if (t == String.class)
                  return (UnaryOperator<T>) aStringThing;
                  if (t == Double.class)
                  return (UnaryOperator<T>) aDoubleThing;
                  return null;
                  }


                  In those cases, we actually know the cast is correct as long as nobody else is doing something funny (like unchecked casting the Class<T> argument).



                  So in the general case of casting to UnaryOperator<T>, we might actually be doing something legitimate. In comparison, with the case of casting List<String> to List<Double>, we can say pretty authoritatively that it's always wrong.






                  share|improve this answer




























                    4












                    4








                    4







                    This cast compiles, because it's a special case of a narrowing conversion. (According to §5.5, narrowing conversions are one of the types of conversions allowed by a cast, so most of this answer is going to focus on the rules for narrowing conversions.)



                    Note that while UnaryOperator<T> is not a subtype of UnaryOperator<Object> (so the cast isn't a "downcast"), it's still considered a narrowing conversion. From §5.6.1:




                    A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. [...] Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.




                    Some of these "sideways" casts fail due to special rules, for example the following will fail:



                    List<String> a = ...;
                    List<Double> b = (List<String>) a;


                    Specifically, this is given by a rule in §5.1.6.1 which states that:






                    • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).



                      Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.






                    In other words, if a and b have a common supertype with the same erasure (in this case, for example, List), then they must be what the JLS is calling "provably distinct", given by §4.5:




                    Two parameterized types are provably distinct if either of the following is true:




                    • They are parameterizations of distinct generic type declarations.


                    • Any of their type arguments are provably distinct.





                    And §4.5.1:




                    Two type arguments are provably distinct if one of the following is true:




                    • Neither argument is a type variable or wildcard, and the two arguments are not the same type.


                    • One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S|.


                    • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.





                    So, given the above rules, List<String> and List<Double> are provably distinct (via the 1st rule from 4.5.1), because String and Double are different type arguments.



                    However, UnaryOperator<T> and UnaryOperator<Object> are not provably distinct (via the 2nd rule from 4.5.1), because:




                    1. One type argument is a type variable (T, with a bound of Object.)


                    2. The bound of that type variable is the same as the type argument to the other type (Object).



                    Since UnaryOperator<T> and UnaryOperator<Object> are not provably distinct, the narrowing conversion is allowed, hence the cast compiles.





                    One way to think about why the compiler permits some of these casts but not others is: in the case of the type variable, it can't prove that T definitely isn't Object. For example, we could have a situation like this:



                    UnaryOperator<String> aStringThing = Somewhere::doStringThing;
                    UnaryOperator<Double> aDoubleThing = Somewhere::doDoubleThing;

                    <T> UnaryOperator<T> getThing(Class<T> t) {
                    if (t == String.class)
                    return (UnaryOperator<T>) aStringThing;
                    if (t == Double.class)
                    return (UnaryOperator<T>) aDoubleThing;
                    return null;
                    }


                    In those cases, we actually know the cast is correct as long as nobody else is doing something funny (like unchecked casting the Class<T> argument).



                    So in the general case of casting to UnaryOperator<T>, we might actually be doing something legitimate. In comparison, with the case of casting List<String> to List<Double>, we can say pretty authoritatively that it's always wrong.






                    share|improve this answer















                    This cast compiles, because it's a special case of a narrowing conversion. (According to §5.5, narrowing conversions are one of the types of conversions allowed by a cast, so most of this answer is going to focus on the rules for narrowing conversions.)



                    Note that while UnaryOperator<T> is not a subtype of UnaryOperator<Object> (so the cast isn't a "downcast"), it's still considered a narrowing conversion. From §5.6.1:




                    A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. [...] Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.




                    Some of these "sideways" casts fail due to special rules, for example the following will fail:



                    List<String> a = ...;
                    List<Double> b = (List<String>) a;


                    Specifically, this is given by a rule in §5.1.6.1 which states that:






                    • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).



                      Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.






                    In other words, if a and b have a common supertype with the same erasure (in this case, for example, List), then they must be what the JLS is calling "provably distinct", given by §4.5:




                    Two parameterized types are provably distinct if either of the following is true:




                    • They are parameterizations of distinct generic type declarations.


                    • Any of their type arguments are provably distinct.





                    And §4.5.1:




                    Two type arguments are provably distinct if one of the following is true:




                    • Neither argument is a type variable or wildcard, and the two arguments are not the same type.


                    • One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S|.


                    • Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S| <: |T| nor |T| <: |S|.





                    So, given the above rules, List<String> and List<Double> are provably distinct (via the 1st rule from 4.5.1), because String and Double are different type arguments.



                    However, UnaryOperator<T> and UnaryOperator<Object> are not provably distinct (via the 2nd rule from 4.5.1), because:




                    1. One type argument is a type variable (T, with a bound of Object.)


                    2. The bound of that type variable is the same as the type argument to the other type (Object).



                    Since UnaryOperator<T> and UnaryOperator<Object> are not provably distinct, the narrowing conversion is allowed, hence the cast compiles.





                    One way to think about why the compiler permits some of these casts but not others is: in the case of the type variable, it can't prove that T definitely isn't Object. For example, we could have a situation like this:



                    UnaryOperator<String> aStringThing = Somewhere::doStringThing;
                    UnaryOperator<Double> aDoubleThing = Somewhere::doDoubleThing;

                    <T> UnaryOperator<T> getThing(Class<T> t) {
                    if (t == String.class)
                    return (UnaryOperator<T>) aStringThing;
                    if (t == Double.class)
                    return (UnaryOperator<T>) aDoubleThing;
                    return null;
                    }


                    In those cases, we actually know the cast is correct as long as nobody else is doing something funny (like unchecked casting the Class<T> argument).



                    So in the general case of casting to UnaryOperator<T>, we might actually be doing something legitimate. In comparison, with the case of casting List<String> to List<Double>, we can say pretty authoritatively that it's always wrong.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 16 '18 at 17:17

























                    answered Nov 16 '18 at 16:38









                    RadiodefRadiodef

                    32.2k1268100




                    32.2k1268100























                        3














                        The JLS allows such cast:




                        A cast from a type S to a parameterized type T is unchecked
                        unless at least one of the following conditions holds:




                        • S <: T


                        • All of the type arguments of T are unbounded wildcards.


                        • [ ... ]





                        As a result, an unchecked cast causes a compile-time unchecked warning, unless suppressed by the SuppressWarnings annotation.



                        Furthermore, during the type erasure process, identifyFunction and IDENTIFY_FN compiles into:



                        private static UnaryOperator IDENTIFY_FN;

                        public static UnaryOperator identifyFunction() {
                        return IDENTIFY_FN; // cast is removed
                        }


                        and the checkcast is added to the call site:



                        System.out.println(sameString.apply(s));
                        ^
                        INVOKEINTERFACE java/util/function/UnaryOperator.apply (Ljava/lang/Object)Ljava/lang/Object
                        CHECKCAST java/lang/String
                        INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V


                        checkcast succeeds, because the identity function returns its argument unmodified.






                        share|improve this answer






























                          3














                          The JLS allows such cast:




                          A cast from a type S to a parameterized type T is unchecked
                          unless at least one of the following conditions holds:




                          • S <: T


                          • All of the type arguments of T are unbounded wildcards.


                          • [ ... ]





                          As a result, an unchecked cast causes a compile-time unchecked warning, unless suppressed by the SuppressWarnings annotation.



                          Furthermore, during the type erasure process, identifyFunction and IDENTIFY_FN compiles into:



                          private static UnaryOperator IDENTIFY_FN;

                          public static UnaryOperator identifyFunction() {
                          return IDENTIFY_FN; // cast is removed
                          }


                          and the checkcast is added to the call site:



                          System.out.println(sameString.apply(s));
                          ^
                          INVOKEINTERFACE java/util/function/UnaryOperator.apply (Ljava/lang/Object)Ljava/lang/Object
                          CHECKCAST java/lang/String
                          INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V


                          checkcast succeeds, because the identity function returns its argument unmodified.






                          share|improve this answer




























                            3












                            3








                            3







                            The JLS allows such cast:




                            A cast from a type S to a parameterized type T is unchecked
                            unless at least one of the following conditions holds:




                            • S <: T


                            • All of the type arguments of T are unbounded wildcards.


                            • [ ... ]





                            As a result, an unchecked cast causes a compile-time unchecked warning, unless suppressed by the SuppressWarnings annotation.



                            Furthermore, during the type erasure process, identifyFunction and IDENTIFY_FN compiles into:



                            private static UnaryOperator IDENTIFY_FN;

                            public static UnaryOperator identifyFunction() {
                            return IDENTIFY_FN; // cast is removed
                            }


                            and the checkcast is added to the call site:



                            System.out.println(sameString.apply(s));
                            ^
                            INVOKEINTERFACE java/util/function/UnaryOperator.apply (Ljava/lang/Object)Ljava/lang/Object
                            CHECKCAST java/lang/String
                            INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V


                            checkcast succeeds, because the identity function returns its argument unmodified.






                            share|improve this answer















                            The JLS allows such cast:




                            A cast from a type S to a parameterized type T is unchecked
                            unless at least one of the following conditions holds:




                            • S <: T


                            • All of the type arguments of T are unbounded wildcards.


                            • [ ... ]





                            As a result, an unchecked cast causes a compile-time unchecked warning, unless suppressed by the SuppressWarnings annotation.



                            Furthermore, during the type erasure process, identifyFunction and IDENTIFY_FN compiles into:



                            private static UnaryOperator IDENTIFY_FN;

                            public static UnaryOperator identifyFunction() {
                            return IDENTIFY_FN; // cast is removed
                            }


                            and the checkcast is added to the call site:



                            System.out.println(sameString.apply(s));
                            ^
                            INVOKEINTERFACE java/util/function/UnaryOperator.apply (Ljava/lang/Object)Ljava/lang/Object
                            CHECKCAST java/lang/String
                            INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V


                            checkcast succeeds, because the identity function returns its argument unmodified.







                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Nov 16 '18 at 23:56

























                            answered Nov 16 '18 at 12:50









                            OleksandrOleksandr

                            9,59044272




                            9,59044272























                                1














                                The cast



                                return (UnaryOperator<T>) IDENTIFY_FN;


                                basically amounts to a cast to the raw type UnaryOperator, because T is erased at runtime and ignored for the purpose of casts at compile-time. You can cast a generic type to its raw type (for backwards compatibility reasons), but you should get an "unchecked" warning.



                                This would also work, for example:



                                UnaryOperator<String> foo = (UnaryOperator) IDENTITY_FN;





                                share|improve this answer






























                                  1














                                  The cast



                                  return (UnaryOperator<T>) IDENTIFY_FN;


                                  basically amounts to a cast to the raw type UnaryOperator, because T is erased at runtime and ignored for the purpose of casts at compile-time. You can cast a generic type to its raw type (for backwards compatibility reasons), but you should get an "unchecked" warning.



                                  This would also work, for example:



                                  UnaryOperator<String> foo = (UnaryOperator) IDENTITY_FN;





                                  share|improve this answer




























                                    1












                                    1








                                    1







                                    The cast



                                    return (UnaryOperator<T>) IDENTIFY_FN;


                                    basically amounts to a cast to the raw type UnaryOperator, because T is erased at runtime and ignored for the purpose of casts at compile-time. You can cast a generic type to its raw type (for backwards compatibility reasons), but you should get an "unchecked" warning.



                                    This would also work, for example:



                                    UnaryOperator<String> foo = (UnaryOperator) IDENTITY_FN;





                                    share|improve this answer















                                    The cast



                                    return (UnaryOperator<T>) IDENTIFY_FN;


                                    basically amounts to a cast to the raw type UnaryOperator, because T is erased at runtime and ignored for the purpose of casts at compile-time. You can cast a generic type to its raw type (for backwards compatibility reasons), but you should get an "unchecked" warning.



                                    This would also work, for example:



                                    UnaryOperator<String> foo = (UnaryOperator) IDENTITY_FN;






                                    share|improve this answer














                                    share|improve this answer



                                    share|improve this answer








                                    edited Nov 16 '18 at 12:20

























                                    answered Nov 16 '18 at 12:10









                                    OhleCOhleC

                                    1,840921




                                    1,840921






























                                        draft saved

                                        draft discarded




















































                                        Thanks for contributing an answer to Stack Overflow!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid



                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.


                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function () {
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53337494%2fhow-does-casting-this-object-to-a-generic-type-work%23new-answer', 'question_page');
                                        }
                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        Xamarin.iOS Cant Deploy on Iphone

                                        Glorious Revolution

                                        Dulmage-Mendelsohn matrix decomposition in Python