tags:

views:

41

answers:

1

I've done some fancy wrapping to avoid unchecked warnings in the past, but after 90 mins of poring over http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html, I can't write the findMatch method below and make it work without @SuppressWarnings("unchecked"). The parameterized class isn't known at compile time.

public interface Matchable<T>
{
    public boolean matches(T toMatch);
}    

public class PlaceForMatching
{
  public static Object findMatch(Object toMatch, Object[] toSearch)
  {
     if(!(toMatch instanceof Matchable)) return null;

     Matchable matchObj = (Matchable)toMatch;
     Class<?> matchClass = matchObj.getClass();

     for(Object obj : toSearch)
     {
        /** 
          *  Check here verifies that the search list object we're about  
          *  to check is the same class as the toMatch object.
          *  This means Matchable will work without a ClassCastException.
         **/

        if(matchClass.isInstance(obj) && matchObj.matches(obj))
           return obj;
     }
     //Didn't find it
     return null;
  }
}

Note the code works because in every case Matchable is implemented by T.

Apple implements Matchable<Apple>
Orange implements Matchable<Orange>

EDIT: Add some test code

public static void main(String[] args)
{
    Object[] randomList = createAppleArray();
    Object apple = new Apple("Red");

    Object match = findMatch(apple, randomList);
}

private static Object[] createAppleArray()
{
    return new Object[] { new Apple("Pink"), new Apple("Red"), new Apple("Green") };
}


public class Apple implements Matchable<Apple>
{
    String color;
    public Apple(String color)
    {
       this.color = color;
    }

    public boolean matches(Apple apple)
    {
       return color.equals(apple.color);
    }
}
+2  A: 
public static <T extends Matchable<T>> T findMatch(T toMatch, T[] toSearch) {
  if (toMatch == null)
    return null;

  Matchable<T> matchObj = toMatch;
  Class<?> matchClass = matchObj.getClass();

  for (T obj : toSearch) {
    if (matchClass.isInstance(obj) && matchObj.matches(obj))
      return obj;
  }

  return null;
}
Roland Illig
This is correct, and I should have been more clear in the question. I need a method signature using non-parameterized types (they've been lost by the time they get to the offending code).Now if there's a way to cast the generic Object so it can be used by this method, I'd love to learn it. The following doesn't work, but this would be the basic idea...` public static <T extends Matchable<T>> Object findMatch(Object toMatch, Object[] toSearch) { return findMatch((T)toMatch, (T[]) toSearch); }`
Steve Jackson
I'm awful at this markup...`public static <T extends Matchable<T>> Object findMatch(Object toMatch, Object[] toSearch)``{` `return findMatch((T)toMatch, (T[]) toSearch);``}`
Steve Jackson
Can you explain why you need your method to accept `Object` and `Object[]` as parameters instead of `Matchable` and `Matchable[]`? Tell us a bit more about the environment in which you use that code. At least I am curious.
Roland Illig
I'm sorry Roland, I didn't see the comment before now.I'm pulling objects out of a JList's DefaultListModel, so types are lost once they're put in, and replacing the model or "unsafe" casting were the options. I thought the casts couldn't be done in such a way that the compiler is satisfied, but I'm often wrong.
Steve Jackson
If your `DefaultListModel` contains only instances of `Matchable`, you could first create an array of `Matchable` and then `copyInto` this array. That would be a silent conversion without compiler warning.
Roland Illig
@Roland - Excellent. Cleans up the code as well as the `isInstance()` check is no longer needed. Thanks for sticking with me.
Steve Jackson