How to extend a Map generics-wise?

H

hiwa

If you are a Java Generics guru, would you please help.
Below is an implementation of a multi-value Map that use
ArrayList as its value element.
What should we do in order for this to be compiled
without errors nor warnings from 1.5 compiler? If you
could attach brief explanation, we won't be happier.
------------------------------------------------------------
import java.util.*;

public class MultiValueLinkedHashMap extends LinkedHashMap{

public MultiValueLinkedHashMap(){
super();
}

public MultiValueLinkedHashMap(int initialCapacity){
super(initialCapacity);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor){
super(initialCapacity, loadFactor);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor, boolean accessOrder){
super(initialCapacity, loadFactor, accessOrder);
}

public MultiValueLinkedHashMap(Map m){
super(m);
}

public Object put(Object key, Object value){
ArrayList al = null;

if (containsKey(key)){ //existing key
al = (ArrayList)get(key);
al.add(value); //just add the new value
}
else{ //fresh entry
al = new ArrayList();
al.add(value);
super.put(key, al);
}
return al; //yes, returns updated value
}
}
------------------------------------------------------------
 
T

Tony Morris

hiwa said:
If you are a Java Generics guru, would you please help.
Below is an implementation of a multi-value Map that use
ArrayList as its value element.
What should we do in order for this to be compiled
without errors nor warnings from 1.5 compiler? If you
could attach brief explanation, we won't be happier.
------------------------------------------------------------
import java.util.*;

public class MultiValueLinkedHashMap extends LinkedHashMap{

public MultiValueLinkedHashMap(){
super();
}

public MultiValueLinkedHashMap(int initialCapacity){
super(initialCapacity);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor){
super(initialCapacity, loadFactor);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor, boolean accessOrder){
super(initialCapacity, loadFactor, accessOrder);
}

public MultiValueLinkedHashMap(Map m){
super(m);
}

public Object put(Object key, Object value){
ArrayList al = null;

if (containsKey(key)){ //existing key
al = (ArrayList)get(key);
al.add(value); //just add the new value
}
else{ //fresh entry
al = new ArrayList();
al.add(value);
super.put(key, al);
}
return al; //yes, returns updated value
}
}
------------------------------------------------------------

Do not extend a concrete class unless you have to*, and you certainly don't
have to in the case of Java 2 Collections where most concrete types expose
all (almost) of their public API through an interface.

Google for "Decorator Design Pattern" and in this case, decorate the
java.util.Map<K, V> interface. It's a much nicer approach.

*
<unpopular-assertion>
Concrete inheritance is evil, and all classes should be declared final
</unpopular-assertion>
 
S

Stefan Schulz

<unpopular-assertion>
Concrete inheritance is evil, and all classes should be declared final
</unpopular-assertion>

You wouldn't get very far following that advice, seeing that Object is
concrete. *ducks*
 
R

Ryan Stewart

Stefan Schulz said:
You wouldn't get very far following that advice, seeing that Object is
concrete. *ducks*
Not that I agree with Tony's comment, but when is the last time you created an
Object for some purpose besides testing something? Semantically speaking, maybe
Object should be abstract.
 
C

Chris Smith

Ryan Stewart said:
Not that I agree with Tony's comment, but when is the last time you created an
Object for some purpose besides testing something? Semantically speaking, maybe
Object should be abstract.

I actually tend to do it quite often. Searching my code (minus tests)
for "new Object()" yields about 12 results. They get used in my code
for any time I need a unique identifier within a single JVM that won't
be persisted across more than one operation. For example, my custom
tree model implementations often include a java.lang.Object instance
that represents a false root for the tree, or something like that. They
can also be used during optimization to break apart more course-grained
locks when the locks are seeing too much contention.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
S

Stefan Schulz

Not that I agree with Tony's comment, but when is the last time you
created an Object for some purpose besides testing something?
Semantically speaking, maybe Object should be abstract.

Anytime i wish a unique, distinct object to synchronize over. For example,
if need to synchronize over three resources in some methods, and over
another, distinct set of other two other resources, i usually create a
"fooLock" and barLock Object.
 
H

hiwa

My question is about proper syntax for Java Generics.
It is not about 'don't extend concrete class' issue.
Besides, we absolutely need to extend LinkedHashMap
because it has a specific ordering characteristics.
I don't want to implement Map interface and write
those characteristics from scratch.

Below is a repost of the original post.
Please note that this is a generics related question,
not a 'concrete class subclassing' one.

Thank you in advance.

............................................................
If you are a Java Generics guru, would you please help.
Below is an implementation of a multi-value Map that use
ArrayList as its value element.
What should we do in order for this to be compiled
without errors nor warnings from 1.5 compiler? If you
could attach brief explanation, we won't be happier.
------------------------------------------------------------
import java.util.*;

public class MultiValueLinkedHashMap extends LinkedHashMap{

public MultiValueLinkedHashMap(){
super();
}

public MultiValueLinkedHashMap(int initialCapacity){
super(initialCapacity);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor){
super(initialCapacity, loadFactor);
}

public MultiValueLinkedHashMap
(int initialCapacity, float loadFactor, boolean accessOrder){
super(initialCapacity, loadFactor, accessOrder);
}

public MultiValueLinkedHashMap(Map m){
super(m);
}

public Object put(Object key, Object value){
ArrayList al = null;

if (containsKey(key)){ //existing key
al = (ArrayList)get(key);
al.add(value); //just add the new value
}
else{ //fresh entry
al = new ArrayList();
al.add(value);
super.put(key, al);
} return al; //yes, returns updated value
}
}
 
S

Stefan Schulz

My question is about proper syntax for Java Generics.
It is not about 'don't extend concrete class' issue.
Besides, we absolutely need to extend LinkedHashMap
because it has a specific ordering characteristics.
I don't want to implement Map interface and write
those characteristics from scratch.

Tony provided an adequate answer to your problem, but just so you know:
The point currently raised is no longer about your original question.

Normally you would get a snide comment from me, but i am feeling
particularly nice today, so here is 2 minutes worth of work.

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class MultiValueHashMap<K, V> extends LinkedHashMap<K, List<V>> {

public MultiValueHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}

public MultiValueHashMap(int initialCapacity) {
super(initialCapacity);
}

public MultiValueHashMap() {
super();
}

public MultiValueHashMap(Map<? extends K, ? extends List<V>> m) {
super(m);
}

public List<V> put(K key, V val){
List<V> rv;
if (!containsKey(key)){
rv = new ArrayList<V>();
put(key, rv);
} else {
rv = get(key);
}

rv.add(val);
return rv;
}
}

Just out of idle curiosity, is that really what you want? After all, you
do still inherit the list-based put. Overwriting that is just not
type-safe, since you break the assumption that the value object does end
up as the mapped value. However, this is the best you can usually do.
 
S

Stefan Schulz

put(key, rv);

this should be:

super.put(key, rv);

Why didn't i get a compiler error for this one? I would consider it a bug,
since the generic type V is not compatible with List<V> - i originally
thought the compiler would be intelligent enough to figure out that i
meant the super method, but examining the bytecode shows it definitly does
not do it.

Anyway, for generics, i can suggest you have a look at
http://www.langer.camelot.de/GenericsFAQ/JavaGenericsFAQ.html
which introduces the new concepts a little more gently then the official
tutorial, and spends a lot of time on the less clear points
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top