views:

81

answers:

1

Alright, I thought I understood generics pretty well, but for some reason I can't get my head wrapped around why this doesn't work. I have two classes, or I should say that Google has two classes (I'm trying to implement their Contacts API). They have a ContactEntry class (abbreviated below):

package com.google.gdata.data.contacts;

public class ContactEntry extends BasePersonEntry<ContactEntry> {

  public ContactEntry() {
    super();
    getCategories().add(CATEGORY);
  }

  public ContactEntry(BaseEntry<?> sourceEntry) {
    super(sourceEntry);
  }

}

I left off one or two methods, but nothing important, its really just an implementation of its parent class BasePersonEntry which has most of the important stuff that concerns a person entry abbreviated code below:

package com.google.gdata.data.contacts;

public abstract class BasePersonEntry<E extends BasePersonEntry> extends
    BaseEntry<E> {

  public BasePersonEntry() {
    super();
  }

  public BasePersonEntry(BaseEntry<?> sourceEntry) {
    super(sourceEntry);
  }

  public List<CalendarLink> getCalendarLinks() {
    return getRepeatingExtension(CalendarLink.class);
  }

  public void addCalendarLink(CalendarLink calendarLink) {
    getCalendarLinks().add(calendarLink);
  }

  public boolean hasCalendarLinks() {
    return hasRepeatingExtension(CalendarLink.class);
  }

}

Now... what I can't quite understand is if I do something like the following:

public void method1(StringBuilder sb, ContactEntry contact) {
 if (contact.hasCalendarLinks()) {
  for (CalendarLink calendarLink : contact.getCalendarLinks()) {
   ...
  }
 }
}

Everything works fine. It is able to interpret that getCalendarLinks returns a list of type CalendarLink. However, if I want to abstract this method and have my method use BasePersonEntry, like the following:

public void method1(StringBuilder sb, BasePersonEntry entry) {
 if (entry.hasCalendarLinks()) {
  for (CalendarLink calendarLink : entry.getCalendarLinks()) {
   ...
  }
 }
}

I get a compiler error:

incompatible types
found   : java.lang.Object
required: com.google.gdata.data.contacts.CalendarLink

For the life of me I just can't understand why? The call to getCalendarLinks is the EXACT same method (via inheritance), its returning the EXACT same thing. Maybe it has to do with BasePersonEntry being an abstract class?

If anyone, can shed some light on this I would be very much obliged. If it helps you can find a full version of this source code hosted by Google here: Link To Google Library Download. I was attempting this with version 1.41.3 of their gdata-java libraries.

+2  A: 

The problem with your new definition, is that it's using Raw type not Generic type.

As a result type is erased from everything, including getCalendarLinks and its signature is reduced to equivalent of List<Object> getCalendarLinks( )

To fix it, change declaration to:

public void method1(StringBuilder sb, BasePersonEntry<?> entry)

Note <?> after BasePersonEntry. This way it's generic type.

Also, you probably want to change the class generic declaration to

public abstract class BasePersonEntry<E extends BasePersonEntry<E> >

Without <E> your compiler ( or IDE ) will generate an unchecked warning.

Alexander Pogrebnyak
From Shadowcat's initial post, it sounds like BasePersonEntry is a Google class, not one by ShadowCat.
R. Bemrose
It's true, I cannot affect change on the BasePersonEntry, however, everything else that you mentioned is correct. I tested it and it works great with the modification to method1 that you suggested.I was previously only aware of generics as they applied to collections, and was missing a lot of the other applications. Thanks so much for your response!I'm reading over Angelika Langer's Generics FAQ now, it seems I have a lot to learn. = )
Shadowcat
@Shadowcat. I did not notice that BasePersonEntry was a 3rd party class, however when designing your generic base classes try not to follow this example of using Raw types in `extends` statement ( it still produces the `unchecked` warning ). Have fun with generics!
Alexander Pogrebnyak