4

I am trying to create a generic MsgRequest Class that can set/get any type of parameter. The intention here is to create a lightweight container for various types of parameters in a Message and pass it to various method calls.

public class MsgRequest {

//private HashMap<String, String> _params = new HashMap<>();
private final HashMap<String, Object> _params = new HashMap<>();

/**
 * Returns the value of a specified key, if found, null otherwise.
 * @param key
 * @return 
 */

public <T> T getX(String key) {
    return (T) _params.get(key);
}


/**
 * Sets / replaces a given key in Message params.
 * @param <T>
 * @param key 
 */
public <T> void setX(String key, T element) {
    //TODO: Implement 2nd param as a generic type.
    //_params.put(key, element);
}

When I try to test this class like below,

@Test
public void testGetString() {
    MsgRequest msg = new MsgRequest();
    String key = "one";
    String val = "This is one.";
    msg.setX(key, val);
    //String s = msg.getX("one");
    assertTrue("result should be string type", msg.getX("one") instanceof String);
}

Then it throws java.lang.NoSuchMethodError.

Testcase: testGetString(com.msgx.MsgRequest.MsgRequestTest): Caused an ERROR com.msgx.MsgRequest.MsgRequest.setX(Ljava/lang/String;Ljava/lang/Object;)V java.lang.NoSuchMethodError: com.msgx.MsgRequest.MsgRequest.setX(Ljava/lang/String;Ljava/lang/Object;)V at com.msgx.MsgRequest.MsgRequestTest.testGetString(MsgRequestTest.java:48)

Unable to figure out how to fix this exception. Any suggestions?

Mopparthy Ravindranath
  • 3,014
  • 6
  • 41
  • 78
  • could you derive what `T` is if i just show you `msg.getX("one")`? – Jason Hu Dec 09 '14 at 18:56
  • 3
    Compiles and runs fine here. Which Java version and compiler are you using? Are you sure you have recompiled your test after making a change to the MsgRequest class? – JB Nizet Dec 09 '14 at 18:58

1 Answers1

2

Make the class MsgRequest generic like

public class MsgRequest<T> {
    private final Map<String, T> _params = new HashMap<>();

    /**
     * Returns the value of a specified key, if found, null otherwise.
     * @param key
     * @return 
     */
    public T getX(String key) {
        return _params.get(key);
    }

    /**
     * Sets / replaces a given key in Message params.
     * @param <T>
     * @param key 
     */
    public void setX(String key, T element) {
        _params.put(key, element);
    }
}

And then use it like

@Test
public void testGetString() {
    MsgRequest<String> msg = new MsgRequest<>();
    String key = "one";
    String val = "This is one.";
    msg.setX(key, val);
    assertTrue("result should be string type", 
        msg.getX("one") instanceof String);
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • I think your code works only for storing uniformly typed objects in the map. What if I wanted a Msg object that can store heterogeneous types? I am not worried much about type safety, but wanted a generic container. – Mopparthy Ravindranath Dec 09 '14 at 19:08
  • 1
    @MupparthyRavindranath Then you can't use generics. Generics are for compile time type checking and homogeneous types. At run-time, they're erased. – Elliott Frisch Dec 09 '14 at 19:09
  • I am trying to do something similar as in this question. http://stackoverflow.com/questions/450807/how-do-i-make-the-method-return-type-generic But getting NoSuchMethodError. – Mopparthy Ravindranath Dec 09 '14 at 19:26
  • You didn't pass the Class in. It's not optional. Go read the answer to that question again. – Elliott Frisch Dec 09 '14 at 19:28
  • You are right. I changed now. But the point is, I get an exception, even before I hit the msg.getX(...) method. The test fails at setX method with that exception. – Mopparthy Ravindranath Dec 09 '14 at 19:32