Skip to main content

Unique maps

6 replies [Last post]
subanark
Offline
Joined: 2004-11-26
Points: 0

Im couldn't find any class similar to this in tiger (correct me if Im wrong). I would like to see a class like this:

public interface UniqueMap
{
public static class Identifier
{}
E get(Identifier identifier);
void put(Identifier identifier, E value);
}

This way you can put items into a set with a given type and take them out without casting (assuming that there are multiple types in the set).

Message was edited by: subanark

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tackline
Offline
Joined: 2003-06-19
Points: 0

Isn't it easy enough to whip something up? Without testing:

public class TypeMap {
private final Map, V> map;
public TypeMap() {
this(new HashMap, V>());
}
public TypeMap(Map, V> map) {
this.map = map;
}
public T get(Class clazz) {
return clazz.cast(map.get(clazz));
}
public void put(Class clazz, T value) {
assert value == null || clazz.isInstance(value);
map.put(class, value);
}
}

Not that casting is so bad:

Map, Object> map = new HashMap, Object>();
map.put(Class.MyType, myObject);
MyType myObject = (MyType)map.get(Class.MyType);

subanark
Offline
Joined: 2004-11-26
Points: 0

I want to avoid casting, since the compiler will not catch casting mistakes. I am not discussing enforcing typecasting.

yishai
Offline
Joined: 2003-11-16
Points: 0

> Im couldn't find any class similar to this in tiger
> (correct me if Im wrong). I would like to see a class
> like this:
>
> public interface UniqueMap
> {
> public static class Identifier
> {}
> E get(Identifier identifier);
> void put(Identifier identifier, E value);
> }
>
> This way you can put items into a set with a given
> type and take them out without casting (assuming that
> there are multiple types in the set).

That code doesn't work. Try this:

[pre]
public interface UniqueMap {
public static class Identifier {
}

E get(Identifier identifier);

void put(Identifier identifier, E value);
}

public static void main(String[] args) throws Exception {
UniqueMap map = new UniqueMap() {
public E get(Identifier identifier) {
return null;
}

public void put(Identifier identifier, E value) {

}
};

UniqueMap.Identifier id = new UniqueMap.Identifier();
map.put(id, new Object());
UniqueMap.Identifier sid = new UniqueMap.Identifier();
String s = map.get(sid);
}
[/pre]

The generics did nothing for you there. You will still need to cast.

Anyway, even if you could get something like that done with generics, what would be the point? You would still need to know the type of the identifier. How is that different from managing a cast?

subanark
Offline
Joined: 2004-11-26
Points: 0

I do see some mistakes in your code. This is what I'm using and it works fine.
[code]
public class GameStateImpl extends Hashtable,Object> implements UniqueMap

{ /** Creates a new instance of GameStateImpl */ public GameStateImpl() { } @SuppressWarnings(value="unchecked") public E put(Identifier identifier, E value) { return (E)super.put(identifier,value); } @SuppressWarnings(value="unchecked") public E get(Identifier identifier) { return (E)super.get(identifier); } public static void main(String[] args) { GameStateImpl gameStateImpl = new GameStateImpl(); UniqueMap.Identifier bident = new UniqueMap.Identifier("bident"); gameStateImpl.put(bident,true); boolean value = gameStateImpl.get(bident); } } [/code] Notice no casting in main at all.
yishai
Offline
Joined: 2003-11-16
Points: 0

> I do see some mistakes in your code. This is what I'm
> using and it works fine.
[snip]
> Notice no casting in main at all.

[code]

public interface UniqueMap {
public static class Identifier {
}

E get(Identifier identifier);

E put(Identifier identifier, E value);
}

public class GameStateImpl extends Hashtable, Object> implements UniqueMap

{ /** * Creates a new instance of GameStateImpl */ public GameStateImpl() { } @SuppressWarnings(value = "unchecked") public E put(Identifier identifier, E value) { return (E) super.put(identifier, value); } @SuppressWarnings(value = "unchecked") public E get(Identifier identifier) { return (E) super.get(identifier); } public static void main(String[] args) { GameStateImpl gameStateImpl = new GameStateImpl(); UniqueMap.Identifier bident = new UniqueMap.Identifier(); gameStateImpl.put(bident, "abc"); boolean value = gameStateImpl.get(bident); } } [/code] No casting, but the code fails when you run it. I think the generic type you are wipping up is misleading you. The E in put is not the E in the get. And people wonder why people hate generics. I guess to continue this, we would need first an example of something that the JDK would actually do. In your code example, besides not actually working, the implementation has to do the cast, so the interface by itself isn't buying much. But even assuming you posted an example which works, how would you know which Identifier instances you have? And why is managing them any more reliable than a cast? At least with a cast, you can't fool yourself.
subanark
Offline
Joined: 2004-11-26
Points: 0

Hrm... well I guess put doesn't work unless you type the object you are getting. But assuming that put operaterations are performed correctly then the get operations will work correctly.

You would know what kind of identifier you have since you declared it. The assumption is that the identifier will be static final. In that you use this to have a repository of properties that multiple classes can access and share data.

This is still using easure in java, Im not here to discuss the advantages and disadvantages of easure. You can just as easily make an "enforced" implementation where the Identifier takes a Class as a parameter and put operations ensure the assignability of of the objects.

What I mainly want to avoid is this:
Foo foo1 = (Foo)table.get("Foo's identifier);

since the compiler cannot check this at compile time, you will end up with a runtime error if an incorrect value is in the table.
If at some latter time you decide that the value in the table must be more broad or different from Foo, then this method could easily miss the refactoring process. If that is the case this method will still compile, but you will not see it fail until it is used at runtime. If this method is a rarely used one the bug could propergate for a long time. If using my approach you change the identifier's type then this method would no longer compile alerting you to the error sooner.

I guess the reason the put operation does not work is the the compiler mearly checks the return type spefied by the user (or Object if none) and sees if one of its subclasses could work [b]for each parameter[/b]. The end result is that if you put in incompadible pieces into the method, the method promises the (almost) impossible, a returned object which is an instance of both parameters. Only null satifies this.

Message was edited by: subanark