[jdom-interest] JDom and Java5

Rolf Lear jdom at tuis.net
Tue Feb 26 07:16:26 PST 2008


Hi Victor,

I learned something new on that, I was unaware that you could have generic types with no 'context' for that type.... that it was based on the left-side of the expression...

so, I changed the code (abstract class and implementation) to the form:

   public <T> List<T> selectNodes(Object context) throws JDOMException {
       try {
          currentContext = context;
          return xPath.selectNodes(context);
       } catch (JaxenException ex1) {
          throw new JDOMException("XPath error while evaluating \"" + xPath.toString() + "\": " + ex1.getMessage(), ex1);
       } finally {
          currentContext = null;
       }
    }

and get the compile time warning on the unchecked conversion on the return line, and, the @suppress would remove the warning like you said. Unfortunately, I don't believe that is enough for an API.

The API is like a contract, and, in the context given, it would really be wrong for there to be class-cast exceptions when the user retrieves a List<Element> but gets a ClassCastException when there happens to be an Attribute in there..... Once the data leaves the API the user should be able to trust that it is right. Is there a way to validate the content of the list prior to returning it? In the 

A second issue comes from the method:

   public <T> T selectSingleNode(Object context) throws JDOMException {
      try {
         currentContext = context;

         return xPath.selectSingleNode(context);
      }
      catch (JaxenException ex1) {
         throw new JDOMException("XPath error while evaluating \"" +
                        xPath.toString() + "\": " + ex1.getMessage(), ex1);
      }
      finally {
         currentContext = null;
      }
   }

Since you have shown me a new way Generics can be used, I'd like to see how it applies to this method. I tried the above, but get "Type mismatch: Can not convert from Object to T". Adding an explicit cast to (T) reduces the compile error to the unchecked compile warning.

Again, my only reluctance so far is that it brings the burden of type checking out of the API and to the user, and the spirit of Generics is that type checking should not be required (or at least reduced) because it is guaranteed by the compiler (through compile warnings or errors...).

Thanks for the feedback. This is going to require some time for me to digest. I need to look in to your suggestions for Filter as well, but will need some digestion time for that as well....

Rolf


On Tue, 26 Feb 2008 15:49:02 +0100, Victor Toni <victor.toni at ebuconnect.de> wrote:
> Hi Rolf,
> 
> Rolf Lear wrote:
>> Hi Victor
>>
>> I have had a look at your suggestion, and I did try to play with similar
> lines of thought, but they are dead-end paths. Correct me if I am wrong:
>>
>> Here's your suggestion which I have simplified to get rid of the
> exception handling.......
>>
>> public <T> List<T> selectNodes(Object context) {
>>       currentContext = context;
>>       return xPath.selectNodes(context);
>>       currentContext = null;
>> }
>>
>> There are a few issues with this that make it impractical as I see it:
>> 1. even if the code were compilable as-is, it would require an explicit
> cast to work, the return line would actually have to read:
>>       return (List<T>)xPath.selectNodes(context);
>>    which introduces 2 problems because firstly, there would be a
> compile-time warning about unchecked casts, and secondly, there will then
> be ClassCastExceptions in the client code in the cases where the client's
> XPath does not match the types represented by <T> (which will happen...!).
>>
> AFAIK this assumption is not correct, because Generics are more of a
> hack than an assertion, an example:
> 
> xpath = [ create an XPath for "//@*]
> 
> List<Element> elements = xpath.selectNodes(myContext); // no exception
> Element element = elements.get(0);  // will throw a ClassCastException
> 
> IMHO this happens because the generified code would translate to
> something like this (internally):
> 
> List elements = xpath.selectNodes(myContext); // no exception
> Element element = (Element) elements.get(0);  // will throw a
> ClassCastException
> 
> Generics are only to save keystrokes and make written code smaller, when
> decompiling the compiled byte code you would have very similar code to
> the pre-1.5 era.
>> 2. The generic typing you suggest is incomplete. You specify <T>
> List<T>, but then do not actually use the typing anywhere. Your method
> signature, to be useful really requires one of two things, either:
>>       public <T> List<T> selectNodes(Object context, Class<T>
> contenttype) {
>>     and then use explicit contentype.cast(.....) calls inside the method
> to cause valid compiles as well as trap any ClassCastExceptions inside the
> API rather than the client.
>>    or
>>       public List<T> selectNodes(Object context) {
>>    where the entire XPath class is a generic typed class <T>.
> Unfortunately this is very hard to accomplish cleanly because much of the
> work done in XPath is done through static methods, and, Java reflection is
> used to create the XPath instances that do the work. Because runtime
> reflection is used, it is very hard to type the data at compile time.
>>
> Actually the pattern I used is quite common, e.g. as in:
> http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html#emptyList()
> 
> <T> doesn't need to be stated explicitly because it is specified
> implicitly by the left hand side. As mentioned above a
> ClassCastException should only occur when trying to access an element
> from within the returned list.
> 
> If would like to get rid of the casting warning please try a version
> similar to this one:
> 
> Instead of
> 
> public <T> List<T> selectNodes(Object context) {
>       return xPath.selectNodes(context);
> }
> 
> you could write:
> 
> public <T> List<T> selectNodes(Object context) {
>       @SuppressWarnings("unchecked")
>       List<T> typedList = (List<T>) xPath.selectNodes(context);
>       return typedList;
> }
> 
> 
> 
>> I don't want to be critical of your suggestion, but, while it is
> somewhat trivial to consider this problem in isolation, the real complexity
> of the problem can only be appreciated if you have the full code in front
> of you and you try it.
>>
>>
> As I have already tried to convert the whole code base myself I know
> that even after multiple iterations there are issues left (BTW the
> Collections framework has been rewritten three times, and this was done
> by the people who "invented" it).
> One more example of  how this patten could be used. AFAIR you have used
> Generics in the Filter interface:
> 
> public interface Filter<T extends Content> {
>     T filter(Content content)
> }
> 
> Actually this is one of the "not so good" ways to use Generics.
> 1. You are restricting yourself to be able to filter only Content
> instead of applying the restriction in the implementation.
> 2. You are reusing T in the wrong place
> 3. You have changed the semantics of Filter, which leads the next point:
> 4. How do you want to implement NegateFilter?
> 
> As you said "it is somewhat trivial to consider this problem in
> isolation..."
> 
> An alternative  approach (which I would prefer) would  be:
> 
> <S> super class of all objects to be filtered
> <R> class of all matching objects
> 
> public interface Filter<S, R extends S> {
>     boolean matches(final S obj);
> }
> 
> 
>> There is a good chance there is something I have missed so I would be
> grateful if you could try to flesh out your idea further in the code - how
> about a fully functional example ;-)
>>
>> Thanks again.
>>
>> Rolf
>>
> Hope this helps,
> Victor
>>




More information about the jdom-interest mailing list