java - Cannot set value of generic Property in Map -


i trying set value of property in map following error:

the method setvalue(capture#7-of ?) in type writablevalue<capture#7-of ?> not applicable arguments (capture#8-of ?) 

here code:

map<string, property<?>> map1 = new hashmap<string, property<?>>(); map<string, property<?>> map2 = new hashmap<string, property<?>>();  map1.put("key1", new simpleintegerproperty(5)); map1.put("key2", new simplestringproperty("hi")); //i need multiple property types in map, of implement property  map2.put("key1", new simpleintegerproperty(5)); map2.put("key2", new simplestringproperty("hi"));  //i can assume value of properties same key of same type map2.get("key1").setvalue(map1.get("key1").getvalue()); //error map2.get("key2").setvalue(map1.get("key2").getvalue()); //error 

i cannot this, must copied value only:

map2.put("key1", map1.get("key1")); map2.put("key2", map1.get("key2")); 

i can simplify more without map:

property<?> p1 = new simpleintegerproperty(5); property<?> p2 = new simpleintegerproperty(10); p1.setvalue(p2.getvalue());//same (basic) error p1.setvalue(new object());//same (basic) error 

i using java 1.8 jdk

the problem trying inherently not type safe. may know programming logic type of property associated "key1" in map1 same type of property associated "key1" in map2, compiler cannot possibly guarantee fact. current structure have, choice abandon compile-time safety.

the underlying issue here map has wrong api requirements (even though basic functionality need). map homogeneous container, meaning values in given map of same type. enforced api: public v get(k key); , public void put(k key, v value); use same type v, fixed single map instance. want heterogeneous container, in value varies depending on key. want api in v not fixed instances of container, changes each invocation of methods get , put, depending on value of key. need get , put methods generic methods:

public interface container<k> { // k type of key...      public <v> v get(k key) ;     public <v> void put(k key, v value);  } 

an implementation of documented in josh bloch's "effective java", , called "typesafe heterogeneous container" pattern.

start defining type key maintains type corresponding property:

    /**      * @param <t> type associated key      * @param <k> actual type of key      */     public class typedkey<t, k> {         private final class<t> type ;         private final k key ;          public typedkey(class<t> type, k key) {             if (type == null || key == null) {                 throw new nullpointerexception("type , key must non-null");             }             this.type = type ;             this.key = key ;         }          @override         public boolean equals(object o) {             if (o == null) return false ;             if (! (o instanceof typedkey)) {                 return false ;             }             typedkey<?, ?> other = (typedkey<?, ?>) o ;             return other.type.equals(type) && other.key.equals(key);         }          @override         public int hashcode() {             return objects.hash(type, key);         }     } 

here t type of property, , k actual type of key. modify code to

// string key map property<number>: typedkey<number, string> key1 = new typedkey<>(number.class, "key1");  // string key map property<string>: typedkey<string, string> key2 = new typedkey<>(string.class, "key2"); 

now define container class act map. basic idea here have 2 methods:

public <t> void put(typedkey<t, k> key, property<t> property)  public <t> property<t> get(typedkey<t, k> key) 

the implementation pretty straightforward:

    /**      * @param <k> type of key in typedkey      */     public class typedpropertymap<k> {          private final map<typedkey<?, k>, property<?>> map = new hashmap<>();          public <t> void put(typedkey<t, k> key, property<t> property) {             map.put(key, property);         }          @suppresswarnings("unchecked")         public <t> property<t> get(typedkey<t, k> key) {              // virtue of api defined, property associated             // key must property<t> (even though underlying map not know it):              return (property<t>) map.get(key);         }     } 

there couple of subtleties here. because underlying map private, assured way access through our put , get methods. when call get() on underlying map typedkey<t,k>, assured corresponding property must (null or) property<t> (since thing compiler have allowed have been inserted earlier). though compiler not aware of it, guaranteed cast succeeds, , @suppresswarnings justified.

now if create typedpropertymap<k> (k here type of actual key), compile-time guaranteed map.get(key1) returns property<number> (because key1 has compile-time type of typedkey<number, string>) , map.get(key2) returns property<string> (because key2 has compile-time type of typedkey<string, string>).

here's complete runnable example:

import java.util.hashmap; import java.util.map; import java.util.objects;  import javafx.beans.property.property; import javafx.beans.property.simpleintegerproperty; import javafx.beans.property.simplestringproperty;   public class typedpropertymaptest {      public static void main(string[] args) {         typedpropertymap<string> map1 = new typedpropertymap<>();         typedpropertymap<string> map2 = new typedpropertymap<>();          typedkey<number, string> key1 = new typedkey<>(number.class, "key1");         typedkey<string, string> key2 = new typedkey<>(string.class, "key2");          map1.put(key1, new simpleintegerproperty(5));         map1.put(key2, new simplestringproperty("hi"));          map2.put(key1, new simpleintegerproperty());         map2.put(key2, new simplestringproperty());          map2.get(key1).setvalue(map1.get(key1).getvalue());         map2.get(key2).setvalue(map1.get(key2).getvalue());          system.out.println(map2.get(key1).getvalue());         system.out.println(map2.get(key2).getvalue());      }       /**      * @param <t> type associated key      * @param <k> actual type of key      */     public static class typedkey<t, k> {         private final class<t> type ;         private final k key ;          public typedkey(class<t> type, k key) {             if (type == null || key == null) {                 throw new nullpointerexception("type , key must non-null");             }             this.type = type ;             this.key = key ;         }          @override         public boolean equals(object o) {             if (o == null) return false ;             if (! (o instanceof typedkey)) {                 return false ;             }             typedkey<?, ?> other = (typedkey<?, ?>) o ;             return other.type.equals(type) && other.key.equals(key);         }          @override         public int hashcode() {             return objects.hash(type, key);         }     }      /**      * @param <k> type of key in typedkey      */     public static class typedpropertymap<k> {          private final map<typedkey<?, k>, property<?>> map = new hashmap<>();          public <t> void put(typedkey<t, k> key, property<t> property) {             map.put(key, property);         }          @suppresswarnings("unchecked")         public <t> property<t> get(typedkey<t, k> key) {              // virtue of api defined, property associated             // key must property<t> (even though underlying map not know it):              return (property<t>) map.get(key);         }     } } 

note it's hard make observablemap, same reasons outlined in introduction: observablemap interface defines homogeneous methods (which indeed inherited map interface) cannot implement in way meets requirements. can, however, make implement javafx.beans.observable, allow register invalidationlisteners it, , use in bindings:

   public class typedpropertymap<k> implements observable {          private final observablemap<typedkey<?, k>, property<?>> map = fxcollections.observablehashmap();          @override         public void addlistener(invalidationlistener listener) {             map.addlistener(listener);         }          @override         public void removelistener(invalidationlistener listener) {             map.removelistener(listener);         }          // remaining code before...     } 

Comments

Popular posts from this blog

php - failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request -

java - How to filter a backspace keyboard input -

java - Show Soft Keyboard when EditText Appears -