1

Ok, this might be a little hard to explain. I'm looking for a way to build a Hashmap of some sort, which will be populated with keys that I already know (because they are properties of the object I am creating). Each of these known keys will have values that are either a String, an int or float (which will be autoboxed), or - later - some kind of object/ function (I still don't know how I'll do that part, but it'll be further along the way anyway). I also want to set which type accepts each of the key (say key "x" will only accept an Integer type of value, key "equation" will only accept a string, etc.).

Just to give more context, my goal is to build a Tweener utility, as I found no such library in Java, except for SumoTween, that doesn't fit my needs at all. I'm kind of trying to build my classes the way Caurina is. If you have a better alternative that would save me time and trouble, please feel free to share (be it about Tweeners or about my Hashmap question). By the way, I'm doing this Tweener to use it in an Android game I'm building, in order to do animations on bitmap/drawable objects (can't use the animation classes for those afaik).

PS : This is my first question on this website, I hope I don't sound too confused; Please bear with me.

CyberDandy
  • 470
  • 6
  • 17
  • "I'm kind of trying to build my classes the way Caurina is. " -- that makes no sense to me. Caurina is written in ActionScript. Classes in ActionScript, to the extent ActionScript has them, bear little resemblance to classes in Java. Why are you "trying to build my classes the way Caurina is"? – CommonsWare Jul 10 '11 at 23:43
  • Well, I want to do in Java what Caurina can do in ActionScript, basically. So right now I'm trying to find ways to implement that in Java because such things doesn't exist in this language and I need it. – CyberDandy Jul 11 '11 at 08:27

6 Answers6

4

Why are you using a hashmap for this? If you have known "keys", just make a class with those members and then you can type them however you want.

If you really must use a hashmap for some reason, you can just extend it and override the put() method to check for your magic values. But I strongly recommend against that, it's poor design.

jkraybill
  • 3,339
  • 27
  • 32
  • I don't agree that it's poor design, it just an implementation of a HashMap that has different types for it's keys. – Nicklas A. Jul 10 '11 at 23:54
  • 2
    And that is poor design because a java.util.Map shouldn't behave like that. Jkraybill is right, if OP is looking for a data structure that maps a given name to a given data type, then he's looking for an object, not a map. OP even mentions wishing to create an object from it. The only reason not to go with objects is if the structure of desired objects isn't known at compile time. – entonio Jul 11 '11 at 00:03
  • I guess you're right jkraybill. I'll have to think a little bit more about it anyway, but thanks for pointing that out :). I think I'll be going with a simple class, I should have found that out myself. – CyberDandy Jul 11 '11 at 08:47
0

If I understand you correctly, you are basically looking for a HashMap. Is that correct?

PS Yes, I know that using "Object" that way isn't very pretty.

davorb
  • 613
  • 1
  • 9
  • 17
0

I'm not entirely sure if this is what you're asking for, but it sounds like you could use some kind of interface/abstract class for the key and implement/extend it appropriately for each key type, e.g.:

public interface Key {}

private static final class StringKey implements Key {
    private final String value;

    public StringKey(String value) {
        this.value = value;
    }

    // hashCode, equals here
}

// IntegerKey, FloatKey, etc similarly

Then have something like:

HashMap<Key, Integer> map = new HashMap<Key, Integer>();
0

I'd say create a custom class that inherits from say HashMap, on insert you check the specified key/value if it's valid.

public static final HashMap<String, Class<?>> allowedTypes = new HashMap<String, Class<?>>() {{
    put("key1", String.class);
    put("key2", Integer.class);
    // etc
}};

public class CustomHashMap extends HashMap {
    public Object put(Object key, Object value) {
        Class<?> type = allowedTypes(key);

        if(type != null && !type.isInstance(value))
            throw new IllegalArgumentException("Invalid type for key " + key);

        return put(key, value);
    }

    private void PutAll(Map m) {
        for(Entry<?, ?> entry : m.entrySet())
            put(entry.getKey(), entry.getValue());
    }
}
Nicklas A.
  • 6,501
  • 7
  • 40
  • 65
  • Inheriting from hashmap is an extremely bad idea most of the times, because the behavior with put is quite.. unintuitive. Better to just have a private instance of hashmap and overwrite the necessary methods. – Voo Jul 11 '11 at 04:29
  • I agree that having an Object as key instead of the key type is VERY bad and has causes a lot of bugs :) – Nicklas A. Jul 11 '11 at 05:46
  • As Voo said, I'm not sure this would be safe to implement. I'm going to go with a simple class as I said in my reply to the accepted answer. That being said, thank you for your suggestion ! – CyberDandy Jul 11 '11 at 08:51
0

You could have separate maps, one for the Strings; another for the Integers, etc. And when adding to any of these maps, you could add the key to a (global) set, to ensure that no key is duplicated across maps. I suspect that wouldn't meet your needs very well (and of course the lookup is a little more involved), but if it works for you, great.

Maybe a better option is to add another level of indirection. Make your map a Hashmap where YourThing has subclasses for each of the types you want to hold. So, a StringYourThing has a String data member; an IntegerYourThing has an int data member, etc. I wouldn't be surprised to find that YourThing starts to take on functionality that is currently a switch statement somewhere inside another class that is trying to deal consistently with all these different data types. If that class could simply interact with a YourThing, it could get simpler. But it's hard to know without seeing your code.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
  • I though about your first idea, but it seemed to be too much of a mess. About the second one, as was said in other answers, it could imply some nasty bugs. Thank you for your reply though :). – CyberDandy Jul 11 '11 at 08:50
0

I would rather avoid using map directly. What you are looking for is probably something more application specific:

Assume the whole thing you are building is for bunch of settings. Your "Setting" will have a predefined set of key. Each key is predefined to accept corresponding setting value of specific value. If incorrect type is provided, exception will be thrown.

I think something roughly like this is reasonable:

enum SettingValueType {
  public boolean isCorrectType(Object obj) {
    return true if obj is correct type represented by this enum 
  }
  STRING, INT, FLOAT   // add support to other type if u want
};

clsss SettingValue {
  SettingValueType type;
  Object value;
}

class SettingRepo {  // a repository of setting entries
  private Map<String, SettingValueType> allowedKeyAndType;
  private Map<String, Object> settings;

  SettingRepo() {
    // setup allowedKeyAndType programmatically or from config etc, depends on your design
  }

  public SettingValue setSetting(String key, Object value)  {

    SettingValueType valueType = allowedKeyAndType.get(key);
    if (valueType == null) {
      throw new KeyNotAllowedException();
    }

    if (v!alueType.isCorrectType(value) {
      throw new ValueIncorectTypeException();
    }

    return settings.put(key, new SettingValue(valueType, value));
    }
  } 

  public SettingValue getSetting(String key) {
    // u may throw exception if key is not in predefined set

    return settings.get(key);
  }

  // u may consider adding some convinient methods too:
  public String setStringSetting(String key, String value) {
    if alllowedKeyAndType do not contains key {
      throw KeyNOtAllowedException
    }
    if type is not STRING {
      throw IncorrectTypeExceptin
    }
    settings.put(key, new SettingValue(STRING, value))
  }

  public String getStringSetting(String key) {
    // should be straight forward now, right?
  }    
}

There are lots of place that can be improved according to your usage: if ur types are very dynamic, u may make SettingValueType something like a bunch of strategies, or use Class directly. setSetting() can made generic and take Class as extra parameter etc. But at least, this should give u a starting point.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • Ok, that seems like a reasonable way to do what I want to do. I'll try going with a simple class first, but I'll hold on to your idea just in case. Thank you :). – CyberDandy Jul 11 '11 at 08:59