1 2 3 Previous Next 32 Replies Latest reply: Jun 12, 2010 10:09 PM by 843793 Go to original post RSS
      • 15. Re: What does the return type " <T> T" mean?
        843793
        endasil wrote:
        BenSchulz wrote:
        If the caller is expected to downcast to a specific type, then the method would have a return type of Object (or some other common super type of all possible return types).
        Or, taking up the ObjectInputStream.readObject() example from above, the user is no longer forced to make that cast explicit. It is added for him by the compiler.
        You say that as if it's a good thing. That's where we disagree. Generics are meant to give you the basic assurance that "if I don't cast, my code is type-safe (e.g. safe from ClassCastExceptions)". But the compiler taking away the need for the explicit cast makes it seem like the code is type-safe (since there's no explicit cast) while not improving the type-safety one bit. That is not a good thing. It completely subverts the benefits of generics and makes your code unsafe. It's just not a smart way to design a library.
        It is a good thing. Make no mistake about it, anyone can write a type safe program that is utterly wrong. And generics are meant to reduce the slack of the language: Slack is defined as the set of correct programs which are not elements of the language. Pre-Java 5 you could not write a generic list, so you had to annotate an already correct program with casts for the compiler to accept it. (Of course post-Java 5 you have to annotate it with type arguments, but at least the compiler can prove those to be correct.)
        This declaration of readObject() reduces the slack as well, but allows one to write incorrect programs which are also part of the language (let's pretend it was a language extension). However, here's the difference: One (generics) can be decided while the other (implementation of a (de)serialization protocol) can't (Halting Problem). For a turing complete language you can not reduce the slack to Ø without also allowing incorrect programs.
        Frankly, I suspect that your entire argument hinges on the premise that the library consumers are incompetent and unable to tell that a cast will (perhaps) be inserted for them.
        That's not incompetence! It's really non-transparent! Why the heck would you want to do this when returning Object would be completely self-documenting that a cast is happening (because it's explicitly required!) Your argument hasn't actually stated a benefit of such a library design.
        The benefit is that I don't have to write a bunch of boiler plate code in order to get the type checker to look the other way. That's all a cast is doing, telling the compiler to get out of my way.
        Edit: If I don't cast, and there are no type safety warnings or errors, my code is type-safe.
        Yet you'll have a hard time writing such a program that involves deserialization.

        With kind regards
        Ben
        • 16. Re: What does the return type " <T> T" mean?
          843793
          endasil wrote:
          Your readObject():
          -Reduces readability by making it appear that a compile-time check has occurred (through Generics) whereas it's a run-time check which is usually seen through a cast.
          It dramatically increases readability by implicitly inserting the check for me, I have no illusions as to there being any compile time checks. The compiler mathematically can't check it.
          -Fakes type-safety by moving the safety warning to the library, where the user of the library will never see it, and thus
          It's impossible to type-safely write the program, I'm giving up nothing.
          -Tricks the user of the library into thinking their code is type safe when it isn't (since there's no cast).
          So the user of the library is incompetent. Since I know what and who I write my library for I can come up with a dumbed down version if necessary. If I know my library to be used by competent developers though, I see no harm in making it friendlier for them.
          In short, it subverts all of those identified benefits of Generics, and is therefore (in my eyes) a completely incompetent use of Generics.
          Well, I disagree.

          With kind regards
          Ben
          • 17. Re: What does the return type " <T> T" mean?
            DrClap
            A slightly more elaborate example of a method, with type inference, whose purpose is only to allow the user to not explicitly do a cast, is this method of the Class class:
            public <U> Class<? extends U> asSubclass(Class<U> clazz)
            • 18. Re: What does the return type " <T> T" mean?
              843793
              BenSchulz wrote:
              endasil wrote:
              Your readObject():
              -Reduces readability by making it appear that a compile-time check has occurred (through Generics) whereas it's a run-time check which is usually seen through a cast.
              It dramatically increases readability by implicitly inserting the check for me, I have no illusions as to there being any compile time checks. The compiler mathematically can't check it.
              Honestly, I don't know how a reasonable person can argue that it improves readability. I feel like you're just arguing for the sake of arguing, and not really comprehending or responding to my points at this point. But in case you're not, I'll give it one more try:

              This method:
              public Object readObject();
              documents in the signature itself that the return value will point to an object of type Object, or null. The compiler guarantees this.

              Your method:
              public <T> T readObject();
              documents in the signature itself that the return value will point to an object of the type T as inferred by the assignment context, or null. From the user's perspective, the compiler seems to guarantee this. But the signature lies, plain and simple. Don't try to argue any different. If I have this:
              String str = readObject();
              the declaration of readObject tells me I'll get back a String. But I might not. That is completely non-transparent. I'm flabbergasted that you'd try to argue this improves readability.

              As the library designer, you are responsible for creating transparent functions. All I can say is, from what I've heard of what principles matter to you in designing a library, I would certainly avoid ever using a library produced by you. Period.
              • 19. Re: What does the return type " <T> T" mean?
                843793
                endasil wrote:
                This method:
                public Object readObject();
                documents in the signature itself that the return value will point to an object of type Object, or null. The compiler guarantees this.
                But it does not document whether it ever will return, will complete abnormally or set your house on fire. Let's remember, you're not just willy-nilly calling methods because you can provide arguments and you'd like an instance of the return type, as specified in the signature. You want to accomplish something specific like deserializing a String object. You know you want to do that and you know the compiler has no earthly idea you want to do that. It does not grasp the context, you do.
                Your method:
                public <T> T readObject();
                documents in the signature itself that the return value will point to an object of the type T as inferred by the assignment context, or null.
                Exactly, from the context that I conceptually understand and the compiler does not.
                From the user's perspective, the compiler seems to guarantee this. But the signature lies, plain and simple. Don't try to argue any different. If I have this:
                I don't know what user you're talking about, but any user with minimal understanding of complexity theory/decidability will know that the compiler can't guarantee it.
                String str = readObject();
                the declaration of readObject tells me I'll get back a String. But I might not. That is completely non-transparent. I'm flabbergasted that you'd try to argue this improves readability.
                - I understand that, given the serialization protocol, the next object in the stream is a String.
                - I know the compiler or library can not know that.
                - I thus realize that, should the next object not be a String, I'll get a CCE.
                As the library designer, you are responsible for creating transparent functions.
                It's not like a modern Java library is just a bunch of functions, you get some nice JavaDoc which the IDE of your choice will display when you first look for the method. If you want to know about potential error conditions (such as ClassNotFoundException, etc.) you need to read it anyhow.
                All I can say is, from what I've heard of what principles matter to you in designing a library, I would certainly avoid ever using a library produced by you. Period.
                Not sure how this is relevant.

                I'll make it very black and white:
                You lose: type safety (locally)
                You gain: less boiler plate

                However, the following is not type safe either (locally):
                String str = (String)readObject();
                With kind regards
                Ben
                • 20. Re: What does the return type " <T> T" mean?
                  843793
                  DrClap wrote:
                  A slightly more elaborate example of a method, with type inference, whose purpose is only to allow the user to not explicitly do a cast, is this method of the Class class:
                  public <U> Class<? extends U> asSubclass(Class<U> clazz)
                  Of course, that one actually does a valid run-time check. I have no problem with this sort of thing when it's done safely and transparently.
                  • 21. Re: What does the return type " <T> T" mean?
                    608410
                    Slow response sorry (been very busy).

                    >
                    What this method has is a return type of whatever the caller wants. The only way an implementation can comply with that contract is the 3 means already discussed.
                    And there is nothing wrong with that; *ObjectInputStream.readObject() already has the return type of whatever the caller wants:*
                    (my bolding) This is where you seem to be missing a subtle but fundamental distinction.
                    ObjectInputStream.readObject() does not have the return type of whatever the caller wants

                    The type of what ObjectInputStream.readObject() returns is the type of the next object in the stream. Only when that type corresponds to what the user wants does it return what the user wants. The magic voodoo signature under discussion does claim to return what the user wants - which is just stupid.

                    By using a return type of Object this uncertainty (at compile time) about the exact runtime type is made explicit.
                    (a) I doubt anyone is serializing instances of java.lang.Object so (b)
                    just a wee technical "oneupmanship' aside - I doubt anyone is serializing instances of other than java.lang.Object - but I get your point.
                    any call to readObject() is invariably followed by a downcast. (c) Most of the time (in my experience) the result is cast immediately and assigned to a variable or returned:
                    {code}
                    SomeType var = (SomeType) ois.readObject(); // becomes: SomeType var = ois.readObject();
                    // or
                    return (SomeType) ois.readObject(); // becomes: return ois.readObject();
                    {code}

                    Would you disagree with either (a), (b) or (c); if so why?
                    yes but only on technical grounds - generally I don't disagree with what I think you are trying to say.
                    And if not then what is wrong with having the compiler add these thoroughly redundant casts?
                    because the cast is not redundant - it represents the boundary between your program which cares about types, and (in this case) the serialization system and reflection system below that which don't much care about types (ignoring Serializable). The compiler uses this to check for correctness of your program relative to the type system, and human readers use it to understand the program.

                    If you elide the cast you make that boundary implicit and some readers may not see it.


                    >
                    can you come up with an implementation of Impl.someMethod() that always terminates normally with assertions enabled and doesn't compile with unchecked warnings?
                    (I assume you meant to ask for an implementation that does not return null and eventually terminates.)
                    Obviously not as that would leave the type system unsound.
                    So you agree that the method cannot be implemented yet you still argue that it is not incompetent to specify it.
                    But its not necessarily fair for me to pose a programming challenge on you (especially one that I feel is impossible). So alternatively I'll let you propose some semantics for the OP's method signature, and possibly even a few sibling method signatures if necessary to elaborate, and I claim that I can write a better signature for someMethod, better in terms of capturing the semantics and competence with generics.
                    ObjectInputStream.readObject() -- have at it!
                    The existing signature capture the semantics well and is a competent use of generics - in that it doesn't use them in a case where they are inappropriate - and it doesn't abuse them. If you wanted a generic version of readObject for cases where you are expecting one particular type of object then I'd suggest something type safe like this{code}
                    /**
                    Reads the next object from the inputstream and returns it if it is of the
                    specified type, otherwise throws -- maybe IllegalStateException
                    @ returns the Object if of the specified type
                    @ throws IllegalStateException if the next object is not of the specified type
                    */
                    public <T> T readObject(Class<T> expectedType) {
                    Object result = readObject();
                    if(result == null || expectedType.isInstance(result)) {
                    return expectedType.cast(result);
                    } else {
                    throw new IllegalStateException("The Object on the stream is not of expected type");
                    }
                    }
                    {code}
                    >
                    Frankly, I suspect that your entire argument hinges on the premise that the library +consumers+ are incompetent and unable to tell that a cast will (perhaps) be inserted for them.
                    Competent users will realise that the signature is claiming something which is impossible and then have suspicions about the qaulity of the API, less competent users may get sucked in by the claimed voodoo magic. Neither is acceptable.


                    Bruce
                    • 22. Re: What does the return type " <T> T" mean?
                      843793
                      brucechapman wrote:
                      Competent users will realise that the signature is claiming something which is impossible and then have suspicions about the qaulity of the API
                      I expect competent users to realize the signature is claiming something impossible too, as to their conclusions -- to each their own.

                      The 3 people (of who I am one) who use the library I wrote are happy with the fact that I did not try to force something inherently dynamic into the static-type-system-box. Calls to that method are ubiquitous and having to cast everywhere would have been a nightmare.

                      Perhaps I've been drinking the dynamic typing Kool-Aid.

                      With kind regards
                      Ben
                      • 23. Re: What does the return type " <T> T" mean?
                        843793
                        brucechapman wrote:
                        Frankly, I suspect that your entire argument hinges on the premise that the library consumers are incompetent and unable to tell that a cast will (perhaps) be inserted for them.
                        Competent users will realise that the signature is claiming something which is impossible and then have suspicions about the qaulity of the API, less competent users may get sucked in by the claimed voodoo magic. Neither is acceptable.
                        There's also the fact that as soon as somebody links against Ben's library, there is no (plausible) way to statically determine the type-safety of the system. With a cast, a static analysis tool can detect there's a cast and point to it as an area of type unsafety. With Ben's library, the static analysis would call it type-safe when it's really not. That's not a good thing either.
                        • 24. Re: What does the return type " <T> T" mean?
                          843793
                          endasil wrote:
                          There's also the fact that as soon as somebody links against Ben's library, there is no (plausible) way to statically determine the type-safety of the system. With a cast, a static analysis tool can detect there's a cast and point to it as an area of type unsafety. With Ben's library, the static analysis would call it type-safe when it's really not. That's not a good thing either.
                          Static analysis could easily determine what Bruce did in his second post: You can't provide a sensible implementation for a method with such signature which does not involve an unchecked cast. It follows that "a static analysis tool can detect [...] it as an area of type unsafety".

                          If that had made sense it would have been a good argument though. ;)

                          With kind regards
                          Ben
                          • 25. Re: What does the return type " <T> T" mean?
                            608410
                            Ben said
                            If that had made sense it would have been a good argument though. ;)
                            If WHAT had made sense?

                            (or is your comment just another example of a pronoun that can mean whatever the caller wants it to mean? :-) )

                            Bruce
                            • 26. Re: What does the return type " <T> T" mean?
                              843793
                              BenSchulz wrote:
                              endasil wrote:
                              There's also the fact that as soon as somebody links against Ben's library, there is no (plausible) way to statically determine the type-safety of the system. With a cast, a static analysis tool can detect there's a cast and point to it as an area of type unsafety. With Ben's library, the static analysis would call it type-safe when it's really not. That's not a good thing either.
                              Static analysis could easily determine what Bruce did in his second post: You can't provide a sensible implementation for a method with such signature which does not involve an unchecked cast. It follows that "a static analysis tool can detect [...] it as an area of type unsafety".
                              Not without also creating a whole crapload of false positives. For example, this doesn't actually have any type unsafety:
                              public <T> T getNothing() {
                                 return (T) null;
                              }
                              Yet it would necessarily have to also be flagged as unsafe then. As would any use of Collections.emptyList, or any factory method like this:
                              public <T> List<T> createList() {
                                 return new ArrayList<T>();
                              }
                              So sure, you could say it might not be typesafe, just as you could say that the entire program might not be typesafe, but where is the value in such static analysis? I don't think you've thought this one through.
                              • 27. Re: What does the return type " <T> T" mean?
                                843793
                                endasil wrote:
                                public <T> T getNothing() {
                                return (T) null;
                                }
                                Flagging a call to this method would be good in my book, just use the null-literal.
                                Yet it would necessarily have to also be flagged as unsafe then. As would any use of Collections.emptyList, or any factory method like this:
                                public <T> List<T> createList() {
                                return new ArrayList<T>();
                                }
                                That would not need to be flagged at all, it's a completely different signature than what we've been discussing. Only calls to methods whose signature

                                - has a type variable bound to a type parameter of the same method as the return type and
                                - the variable's type can not be inferred from any of the arguments

                                would need to be flagged.

                                With kind regards
                                Ben
                                • 28. Re: What does the return type " <T> T" mean?
                                  843793
                                  brucechapman wrote:
                                  Ben said
                                  If that had made sense it would have been a good argument though. ;)
                                  If WHAT had made sense?
                                  The argument that static analysis tools can't reason about such methods.

                                  With kind regards
                                  Ben
                                  • 29. Re: What does the return type " <T> T" mean?
                                    608410
                                    So the only thing that is not currently detected by the compiler when looking at the method declaration is an abstract method with these characteristics. A concrete implementation is either type safe by always returning null or throwing an exception (so why use a generic return type in the first place), or it does something the compiler can detect as an unchecked cast.

                                    So the currently undetected problem is (not in calling, but in declaring) a method which

                                    - has a type variable bound to a type parameter of the same method as the return type and
                                    - the variable's type can not be inferred from any of the arguments
                                    - and is abstract


                                    So I agree, static analysis tools can easily reason about that.

                                    Bruce