Java 8 Lambda Sort With Variable Method Reference - java

I have a statement:
searchResults.sort(Comparator.comparing(WCCTableRowData::getD));
where getD is an accessor in the class WCCTableRowData and searchResults is a list of WCCTableRowData. The WCCTableRowData class has accessors from getA through getZ. I need to be able to set the sort field on the fly from a passed in variable. Is there an elegant way to do this or will I need a series of if statements or similar?
UPDATE 1
Unfortunately, neither approach in the accepted answer worked though I think in general the direction is correct. With approach 2 I get:
With approach 1, row.getField does not pick up the getField method in WCCTableRowData class and I get similar "does not conform to upper bound(s)" error. I think the error is saying that WCCTableRowData class has to implement Comparable?

One way is to add a method in WCCTableRowData that can be given a field name and returns the value of that field.
class WCCTableRowData {
Comparable<?> getField(String name) { ... }
}
String name = "C";
searchResults.sort(Comparator.comparing(row -> row.getField(name)));
If you don't want to modify the class, then you could set up an external map.
Map<String, Function<WCCTableRowData, Comparable<?>>> getters = new HashMap<>();
getters.put("A", WCCTableRowData::getA);
getters.put("B", WCCTableRowData::getB);
getters.put("C", WCCTableRowData::getC);
String name = "C";
searchResults.sort(Comparator.comparing(getters.get(name)));

One way would be to store method references in a map with keys - values of your variable. It would be analogue of switch statement. You can use guava ImmutableMap.<WCCTableRowData, Comparable<?>>of() to make it a bit nicer.
Update
You can explicitly tell that comparator which is constructed by Comparator.comparing(...) is comparing Comparable :
Comparator.<WCCTableRowData, Comparable>comparing(getters.get(name));
Also you can just store comparators of your object (sounds much more reasonable but adds boilerplate), not functions returning comparables:
Map<String, Comparator<WCCTableRowData>> comparators = new HashMap<>();
comparators.put("A", Comparator.comparing(WCCTableRowData::getX));
comparators.put("B", Comparator.comparing(WCCTableRowData::getY));
String name = "C";
searchResults.sort(comparators.get(name));

Related

Arrays.asList add ArrayList rather than specific members

I have an object which contains some package-private member variables and I'm adding them to a Google Sheets v4 ValueRange in another object. The current code looks a little bit like this:
List<List<Object>> data = new ArrayList<>();
...
/**
* Sets all the values in the ValueRange member variable
* #return the ValueRange object
*/
ValueRange requestBuilder() {
...
//For each case, add it to the value range
for (int i = 0; i < closedCases.size(); i++) {
data.add(
Arrays.asList(
closedCases.get(i).number,
closedCases.get(i).priority,
closedCases.get(i).firstResp,
closedCases.get(i).accName,
closedCases.get(i).subject,
closedCases.get(i).assigned,
closedCases.get(i).lastUpdated,
closedCases.get(i).daysOld,
closedCases.get(i).jiraCase
)
);
}
vr.setValues(data);
return vr;
}
The question that I'm seeking to answer is, is there any way to do Arrays.asList( closeCases.get(i) ) or add some kind of method on the case object to simply fill all that stuff in, rather than calling out each member variable in the Arrays.asList(). I'm also aware I can use a foreach, but would still need to use the same notation for adding items, which is what I'm trying to avoid.
In case anyone is interested, closedCases is just an ArrayList of an object with some strings and doubles in it.
You somehow need to specify what fields go into this list, in what order. If you want to capture all fields, you could use reflection to iterate over the object (potentially choosing declared, not inherited fields, and potentially choosing only package-private fields), as described here.
But that is not the idiomatic way to do it in Java.
Can you change the definition of the "object which contains some package-private member variables" so that instead it has a Map with key-value pairs?
You could add a List field in the object that is held by closedcases and call that field from inside the loop.
For instance, say the object is Foo,
Inside foo, create a field:
ArrayList<String> allFields = new ArrayList<String>{number. priority …… };
Method:
public ArrayList<String> getAll() {
return allFields;
}
And from inside the loop, just do
data.add(closedCases.get(i).getAll());
If the fields are not just string, you could create different arraylist that holds different types of object, which will increase the list again but could be substantially less that what you gave us.

Use enums for array indexes in Java

While reading Effective Java I came across the suggestion to "use enums instead of int constants". In a current project I am doing something similar to that below:
int COL_NAME = 0;
int COL_SURNAME = 1;
table[COL_NAME] = "JONES"
How would I use enums instead to achieve this? Due to the interface I'm forced to use, I must use an int for my index. The example above is just an example. I'm actually using an API that takes an int for index values.
Applying one usefull pattern together with an anti-pattern often fails ;-)
In your case using an array for not-really array-like data provides a problem when you want to replace int constants with enum values.
A clean(er) solution would be something like an EnumMap with the enum values as keys.
Alternatively you could use table[COL_NAME.ordinal()] if you absolutely must.
If some API forces you to pass around int values but you have control over the actual values (i.e. you could pass your own constants), then you could switch to using enum values in your code and convert to/from enum only at the places where your code interfaces with the API. The reverse operation of enumValue.ordinal() is EnumClass.values()[ordinal]).
It sounds like you are trying to use a EnumMap. This is a Map which wraps an array of values.
enum Column {
NAME, SURNAME
}
Map<Column, String> table = new EnumMap<Column, String>(Column.class);
table.put(Column.NAME, "JONES");
String name = table.get(Column.NAME);
This would much simpler if you used a POJO.
classPerson {
String name;
String surname;
}
Person person = new Person();
person.name = "JONES";
String name = person.name;
Depends on the requirements. You could use Enum.ordinal() to convert an enum to an int.
Note that it's not possible to pass an Enum directly as the index.
Edit:
Another possibility would be to use a Map<YourEnum, String> map and then use map.get(EnumValue).
Since you have to use that table and thus can't actually use an EnumMap, I personally think the best solution would be to stick with what you have. In an enum, the ordinal values of the elements are not supposed to have any intrinsic meaning, while in your case they do, since they are used as indices into the table.
The problem you have is not that you are not using enums, it's that you need magic values to extract column data out of a table. In this case, using integer constants is the right tool for the job, unless if you can tackle the underlying problem of that table.
Now you could tackle it by wrapping the table in your own class that accesses it, and use enums in and out that class. But this introduces more code, another layer of indirection and doesn't actually solve any problems for you, except that an enum is a bit easier to maintain than a list of int values (greatly offset by you having to maintain the wrapper you now wrote).
You could consider this work if you are writing a public API that other people will use, since it will avoid having them depend on some magic values that might change over time (tight coupling which will break things). If you are, then a wrapper which uses an EnumMap internally is likely the way to go.
Otherwise, leave it as is.
Depending on the values of your array, you might be looking at an object instead. For examples if your array looks something like this
person[FIRST_NAME] = "Jim Bob"
person[SURNAME] = "Jones"
person[ADDRESS] = "123 ABC St"
person[CITY] = "Pleasantville"
...
Then what you really want is something like this
Person jimBob = new Person("Jim Bob", "Jones");
jimBob.setAddress("123 ABC St", "Pleasantville", "SC");
....
See Refactoring: Replace Array with Object
I have also run into this problem, and having come from the C++ world I wasn't expecting to run into this.
The beauty of enum is that can create a series of related constants with unique values where the actual value is irrelevant. Their use makes code easier to read and guards against variables being set to invalid enum values (especially in Java), but in a situation like this it is a major pain. While you might have "enum Pets { CAT, BIRD, DOG }" and don't really care what value CAT, BIRD, and DOG actually represent, it's so nice and clean to write:
myPets[CAT] = "Dexter";
myPets[BIRD] = "Polly";
MyPets[DOG] = "Boo-Rooh";
In my situation I ended up writing a conversion function where you pass in an enum value and I return a constant. I hate doing this with a passion, but it's the only clean way I know to maintain the convenience and error checking of enum.
private int getPetsValue(Pets inPet) {
int value = 0;
switch (inPet) {
case CAT: value = 0; break;
case BIRD: value = 1; break;
case DOG: value = 2; break;
default:
assert(false);
value = 0;
break;
}
return value;
}
You can define an enum with a constructor like so:
public enum ArrayIndex {
COL_NAME(0), COL_SURNAME(1);
private int index;
private ArrayIndex(int index) {
this.index = index;
}
public int getIndex() {
return this.index;
}
};
And then use it like this:
public static void main (String args[]) {
System.out.println("index of COL_NAME is " + ArrayIndex.COL_NAME.getIndex());
}

Java Comparator given the name of the property to compare

My problem is this; I have to order a table of data. Each row of the table is an object (lets call it TableObject) stored in a List. Each column of data is a property of the class (usually a String).
I have to do the typical ordering of data when the user clicks on any column. So I thought about changing the List to a TreeSet and implementing Comparator in my TableObject.
The problem comes when I try to reorder the TreeSet. The compare is fairly easy at first (cheeking for exceptions in parseInt have been omitted):
public int compare(TableObject to1, TableObject to2){
TableObject t1 = to1;
TableObject t2 = to2;
int result = 1;
if(Integer.parseInt(t1.getId()) == Integer.parseInt(t2.getId())){result=0;}
if(Integer.parseInt(t1.getId()) < Integer.parseInt(t2.getId())){result=-1;}
return result;
}
But when I have to reorder by the text of the data or by other dozens of data that the TableObject has I have a problem.
I do not want to create dozens of compare functions, each for one. I prefer not to use a switch (or a chain of ifs) to decide how to compare the object.
Is there any way to do this in some way (like Reflexive), that doesn't imply that I will write like hundreds of lines of nearly the same code?
Thanks for all!
Bean Comparator should work.
Using reflection the BeanComparator that will allow you to sort on any property that has a zero parameter method that returns the value of the property.
So basically you can sort on any property that has a "getter" method.
What you could do is make the comparator take a String representing the name of the parameter to sort by in its constructor.
Then you could use reflection to sort by the given parameter.
The following code is very dirty. But I think it illustrates the gist of what you would need to do.
public class FieldComparator<T> implements Comparator<T> {
String fieldName;
public FieldComparator(String fieldName){
this.fieldName = fieldName;
}
#Override
public int compare(T o1, T o2) {
Field toCompare = o1.getClass().getField(fieldName);
Object v1 = toCompare.get(o1);
Object v2 = toCompare.get(o2);
if (v1 instanceof Comparable<?> && v2 instanceof Comparable<?>){
Comparable c1 = (Comparable)v1;
Comparable c2 = (Comparable)v2;
return c1.compareTo(c2);
}else{
throw new Exception("Counld not compare by field");
}
}
}
Yes, you could use the reflection API, to get the content of a field based on it's name.
See Field class and especially the Field.get method.
(I wouldn't recommend it though, as reflection is not designed for this type of task.)

java: How can I do dynamic casting of a variable from one type to another?

I would like to do dynamic casting for a Java variable, the casting type is stored in a different variable.
This is the regular casting:
String a = (String) 5;
This is what I want:
String theType = 'String';
String a = (theType) 5;
Is this possible, and if so how? Thanks!
Update
I'm trying to populate a class with a HashMap that I received.
This is the constructor:
public ConnectParams(HashMap<String,Object> obj) {
for (Map.Entry<String, Object> entry : obj.entrySet()) {
try {
Field f = this.getClass().getField(entry.getKey());
f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
} catch (NoSuchFieldException ex) {
log.error("did not find field '" + entry.getKey() + '"');
} catch (IllegalAccessException ex) {
log.error(ex.getMessage());
}
}
}
The problem here is that some of the class' variables are of type Double, and if the number 3 is received it sees it as Integer and I have type problem.
Yes it is possible using Reflection
Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);
but that doesn't make much sense since the resulting object must be saved in a variable of Object type. If you need the variable be of a given class, you can just cast to that class.
If you want to obtain a given class, Number for example:
Object something = new Integer(123);
String theType = "java.lang.Number";
Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
Number obj = theClass.cast(something);
but there is still no point doing it so, you could just cast to Number.
Casting of an object does NOT change anything; it is just the way the compiler treats it.
The only reason to do something like that is to check if the object is an instance of the given class or of any subclass of it, but that would be better done using instanceof or Class.isInstance().
Update
according your last update the real problem is that you have an Integer in your HashMap that should be assigned to a Double. What you can do in this case, is check the type of the field and use the xxxValue() methods of Number
...
Field f = this.getClass().getField(entry.getKey());
Object value = entry.getValue();
if (Integer.class.isAssignableFrom(f.getType())) {
value = Integer.valueOf(((Number) entry.getValue()).intValue());
} else if (Double.class.isAssignableFrom(f.getType())) {
value = Double.valueOf(((Number) entry.getValue()).doubleValue());
} // other cases as needed (Long, Float, ...)
f.set(this, value);
...
(not sure if I like the idea of having the wrong type in the Map)
You'll need to write sort of ObjectConverter for this. This is doable if you have both the object which you want to convert and you know the target class to which you'd like to convert to. In this particular case you can get the target class by Field#getDeclaringClass().
You can find here an example of such an ObjectConverter. It should give you the base idea. If you want more conversion possibilities, just add more methods to it with the desired argument and return type.
Regarding your update, the only way to solve this in Java is to write code that covers all cases with lots of if and else and instanceof expressions. What you attempt to do looks as if are used to program with dynamic languages. In static languages, what you attempt to do is almost impossible and one would probably choose a totally different approach for what you attempt to do. Static languages are just not as flexible as dynamic ones :)
Good examples of Java best practice are the answer by BalusC (ie ObjectConverter) and the answer by Andreas_D (ie Adapter) below.
That does not make sense, in
String a = (theType) 5;
the type of a is statically bound to be String so it does not make any sense to have a dynamic cast to this static type.
PS: The first line of your example could be written as Class<String> stringClass = String.class; but still, you cannot use stringClass to cast variables.
You can do this using the Class.cast() method, which dynamically casts the supplied parameter to the type of the class instance you have. To get the class instance of a particular field, you use the getType() method on the field in question. I've given an example below, but note that it omits all error handling and shouldn't be used unmodified.
public class Test {
public String var1;
public Integer var2;
}
public class Main {
public static void main(String[] args) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("var1", "test");
map.put("var2", 1);
Test t = new Test();
for (Map.Entry<String, Object> entry : map.entrySet()) {
Field f = Test.class.getField(entry.getKey());
f.set(t, f.getType().cast(entry.getValue()));
}
System.out.println(t.var1);
System.out.println(t.var2);
}
}
You can write a simple castMethod like the one below.
private <T> T castObject(Class<T> clazz, Object object) {
return (T) object;
}
In your method you should be using it like
public ConnectParams(HashMap<String,Object> object) {
for (Map.Entry<String, Object> entry : object.entrySet()) {
try {
Field f = this.getClass().getField(entry.getKey());
f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */
} catch (NoSuchFieldException ex) {
log.error("did not find field '" + entry.getKey() + '"');
} catch (IllegalAccessException ex) {
log.error(ex.getMessage());
}
}
}
It works and there's even a common pattern for your approach: the Adapter pattern. But of course, (1) it does not work for casting java primitives to objects and (2) the class has to be adaptable (usually by implementing a custom interface).
With this pattern you could do something like:
Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);
and the getAdapter method in Wolf class:
public Object getAdapter(Class clazz) {
if (clazz.equals(Sheep.class)) {
// return a Sheep implementation
return getWolfDressedAsSheep(this);
}
if (clazz.equals(String.class)) {
// return a String
return this.getName();
}
return null; // not adaptable
}
For you special idea - that is impossible. You can't use a String value for casting.
Your problem is not the lack of "dynamic casting". Casting Integer to Double isn't possible at all. You seem to want to give Java an object of one type, a field of a possibly incompatible type, and have it somehow automatically figure out how to convert between the types.
This kind of thing is anathema to a strongly typed language like Java, and IMO for very good reasons.
What are you actually trying to do? All that use of reflection looks pretty fishy.
Don't do this. Just have a properly parameterized constructor instead. The set and types of the connection parameters are fixed anyway, so there is no point in doing this all dynamically.
For what it is worth, most scripting languages (like Perl) and non-static compile-time languages (like Pick) support automatic run-time dynamic String to (relatively arbitrary) object conversions. This CAN be accomplished in Java as well without losing type-safety and the good stuff statically-typed languages provide WITHOUT the nasty side-effects of some of the other languages that do evil things with dynamic casting. A Perl example that does some questionable math:
print ++($foo = '99'); # prints '100'
print ++($foo = 'a0'); # prints 'a1'
In Java, this is better accomplished (IMHO) by using a method I call "cross-casting".
With cross-casting, reflection is used in a lazy-loaded cache of constructors and methods that are dynamically discovered via the following static method:
Object fromString (String value, Class targetClass)
Unfortunately, no built-in Java methods such as Class.cast() will do this for String to BigDecimal or String to Integer or any other conversion where there is no supporting class hierarchy. For my part, the point is to provide a fully dynamic way to achieve this - for which I don't think the prior reference is the right approach - having to code every conversion. Simply put, the implementation is just to cast-from-string if it is legal/possible.
So the solution is simple reflection looking for public Members of either:
STRING_CLASS_ARRAY = (new Class[] {String.class});
a) Member member = targetClass.getMethod(method.getName(),STRING_CLASS_ARRAY);
b) Member member = targetClass.getConstructor(STRING_CLASS_ARRAY);
You will find that all of the primitives (Integer, Long, etc) and all of the basics (BigInteger, BigDecimal, etc) and even java.regex.Pattern are all covered via this approach. I have used this with significant success on production projects where there are a huge amount of arbitrary String value inputs where some more strict checking was needed. In this approach, if there is no method or when the method is invoked an exception is thrown (because it is an illegal value such as a non-numeric input to a BigDecimal or illegal RegEx for a Pattern), that provides the checking specific to the target class inherent logic.
There are some downsides to this:
1) You need to understand reflection well (this is a little complicated and not for novices).
2) Some of the Java classes and indeed 3rd-party libraries are (surprise) not coded properly. That is, there are methods that take a single string argument as input and return an instance of the target class but it isn't what you think... Consider the Integer class:
static Integer getInteger(String nm)
Determines the integer value of the system property with the specified name.
The above method really has nothing to do with Integers as objects wrapping primitives ints.
Reflection will find this as a possible candidate for creating an Integer from a String incorrectly versus the decode, valueof and constructor Members - which are all suitable for most arbitrary String conversions where you really don't have control over your input data but just want to know if it is possible an Integer.
To remedy the above, looking for methods that throw Exceptions is a good start because invalid input values that create instances of such objects should throw an Exception. Unfortunately, implementations vary as to whether the Exceptions are declared as checked or not. Integer.valueOf(String) throws a checked NumberFormatException for example, but Pattern.compile() exceptions are not found during reflection lookups. Again, not a failing of this dynamic "cross-casting" approach I think so much as a very non-standard implementation for exception declarations in object creation methods.
If anyone would like more details on how the above was implemented, let me know but I think this solution is much more flexible/extensible and with less code without losing the good parts of type-safety. Of course it is always best to "know thy data" but as many of us find, we are sometimes only recipients of unmanaged content and have to do the best we can to use it properly.
Cheers.
So, this is an old post, however I think I can contribute something to it.
You can always do something like this:
package com.dyna.test;
import java.io.File;
import java.lang.reflect.Constructor;
public class DynamicClass{
#SuppressWarnings("unchecked")
public Object castDynamicClass(String className, String value){
Class<?> dynamicClass;
try
{
//We get the actual .class object associated with the specified name
dynamicClass = Class.forName(className);
/* We get the constructor that received only
a String as a parameter, since the value to be used is a String, but we could
easily change this to be "dynamic" as well, getting the Constructor signature from
the same datasource we get the values from */
Constructor<?> cons =
(Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class});
/*We generate our object, without knowing until runtime
what type it will be, and we place it in an Object as
any Java object extends the Object class) */
Object object = (Object) cons.newInstance(new Object[]{value});
return object;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public static void main(String[] args)
{
DynamicClass dynaClass = new DynamicClass();
/*
We specify the type of class that should be used to represent
the value "3.0", in this case a Double. Both these parameters
you can get from a file, or a network stream for example. */
System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0"));
/*
We specify a different value and type, and it will work as
expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and
File.toString() would do. */
System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath"));
}
Of course, this is not really dynamic casting, as in other languages (Python for example), because java is a statically typed lang. However, this can solve some fringe cases where you actually need to load some data in different ways, depending on some identifier. Also, the part where you get a constructor with a String parameter could be probably made more flexible, by having that parameter passed from the same data source. I.e. from a file, you get the constructor signature you want to use, and the list of values to be used, that way you pair up, say, the first parameter is a String, with the first object, casting it as a String, next object is an Integer, etc, but somehwere along the execution of your program, you get now a File object first, then a Double, etc.
In this way, you can account for those cases, and make a somewhat "dynamic" casting on-the-fly.
Hope this helps anyone as this keeps turning up in Google searches.
Try this for Dynamic Casting. It will work!!!
String something = "1234";
String theType = "java.lang.Integer";
Class<?> theClass = Class.forName(theType);
Constructor<?> cons = theClass.getConstructor(String.class);
Object ob = cons.newInstance(something);
System.out.println(ob.equals(1234));
I recently felt like I had to do this too, but then found another way which possibly makes my code look neater, and uses better OOP.
I have many sibling classes that each implement a certain method doSomething(). In order to access that method, I would have to have an instance of that class first, but I created a superclass for all my sibling classes and now I can access the method from the superclass.
Below I show two ways alternative ways to "dynamic casting".
// Method 1.
mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
switch (mUnitNum) {
case 0:
((MyFragment0) mFragment).sortNames(sortOptionNum);
break;
case 1:
((MyFragment1) mFragment).sortNames(sortOptionNum);
break;
case 2:
((MyFragment2) mFragment).sortNames(sortOptionNum);
break;
}
and my currently used method,
// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);
Just thought I would post something that I found quite useful and could be possible for someone who experiences similar needs.
The following method was a method I wrote for my JavaFX application to avoid having to cast and also avoid writing if object x instance of object b statements every time the controller was returned.
public <U> Optional<U> getController(Class<U> castKlazz){
try {
return Optional.of(fxmlLoader.<U>getController());
}catch (Exception e){
e.printStackTrace();
}
return Optional.empty();
}
The method declaration for obtaining the controller was
public <T> T getController()
By using type U passed into my method via the class object, it could be forwarded to the method get controller to tell it what type of object to return. An optional object is returned in case the wrong class is supplied and an exception occurs in which case an empty optional will be returned which we can check for.
This is what the final call to the method looked like (if present of the optional object returned takes a Consumer
getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

What is wrong with this design? Overriding or overloading of java.util.HashMap

This question was asked to me in MS interview. I wanna know the exact design issue in this piece of code. Code was already given, needed to find the design issue.
I have class MyHashMap which extends java HashMap class. In MyHashMap class I have to keep some information of employees. Key in this map will be firstName+lastName+Address .
public MyHashMap extends HashMap<Object, Object> {
//some member variables
//
public void put(String firstName, String lastName, String Address, Object obj) {
String key = firstName + lastName+ Address;
put(key, obj);
}
public Object get(String firstName, String lastName, String Address) {
String key = firstName + lastName+ Address;
return get(key);
}
public void remove(Strig key) {
put(key, "");
}
//some more methods
}
What is wrong with this design? Should I subclass HashMap or should I declare
HashMap as member variable of this class? Or should I have implemented hashCode/equals methods?
There are quite a few problems, but the biggest problem I can see it that you're using a concatenated String as a key. The following two calls are different, but equivalent:
final MyHashMap map = new MyHashMap();
map.put("foo", "", "baz", new Object());
map.put("", "foo", "baz", new Object()); // Overwrites the previous call
There's also an issue that you're declaring that the key type as an Object, but always using String and are therefore not taking advantage of the type safety that comes with generics. For example, if you wanted to loop through the keySet of your Map, you'd have to cast each Set entry to a String, but you couldn't be sure that someone didn't abuse you Map by using an Integer key, for example.
Personally, I would favour composition over inheritance unless you have a good reason not to. In your case, MyHashMap is overloading the standard Map methods of put, get and remove, but not overriding any of them. You should inherit from a class in order to change its behaviour, but your implementation does not do this, so composition is a clear choice.
To act as an example, overloading rather than overriding means that if you make the following declaration:
Map<Object, Object> map = new MyHashMap();
none of your declared methods will be available. As recommended by some of the other answers, it would be far better to use an object composed of firstName, lastName and address to act as your map key, but you must remember to implement equals and hashCode, otherwise your values will not be retrievable from the HashMap.
What's wrong with that design is primarily that is claims to be a HashMap<Oject, Object>, but isn't really. The overloaded methods "replace" the Map methods, but those are still accessible - now you're supposed to use the class in a way that is incompatible with the Map interface and ignore the (still technically possible) compatible way to use it.
The best way to do this would be to make an EmployeeData class with name and address fields and hashCode() and equals() methods based on those (or, better yet, a unique ID field). Then you don't need a non-standard Map subclass - you can simply use a HashMap<EmployeeData, Object>. Actually, the value type should be more specific than Object as well.
I would go for declaring HashMap as member variable of your class.
I don't remember if a great explanation is given in Clean Code or Effective Java, but basically, it's simplier to do, requires less work if the API changes.
generally, extending such a class means you want to change its behavior.
The map uses an internal key based on first name, last name and address. So the remove method should (1) be implemented as remove(String firstName, String lastName, String Address) and (2) it should not change the behaviour of the original remove method (which really deleted the entry) by just changing the value. A better implementation of remove would be:
public void remove(String firstName, String lastName, String address) {
String key = firstName + lastName + address;
return remove(key);
}
My first take would be that:
public MyHashMap extends HashMap<Oject, Object>
is wrong. No type safety.
If a Map is required then at least make it
public MyHashMap extends HashMap<NameAddress, Employee>
and adjust the put and get the same. A NameAddress use firstname, lastname and address
in the equals and hashCode.
I personally feel that a Set interface would match better, with possibly a HashMap as a member. Then you could define the interface like it should be:
public EmployeeSet implements Set<Employee> {
private static class NameAddress {
public boolean equals();
public int hashCode();
}
private HashMap employees = new HashMap<NameAddress, Employee>();
public add(Employee emp) {
NameAddress nad = new NameAddress(emp);
empoyees.add(nad, emp);
}
public remove(Employee emp) {
}
}
and extract name and address info within the implementation to create the key.
EDIT David pointed out that NameAddress doesn't need to be Comparable and I removed that part of the interface. Added hashCode for correctness.
Two other "heinous crimes" against good practice are that the remove method:
overloads Map.remove(<K>) rather than overriding it (so you have a leaky abstraction), and
implements a behavior that is incompatible with the behavior of the method it overrides!
Setting the value associated with a key to an empty string is NOT THE SAME as removing the entry for the key. (Even setting it to null is subtly different ...)
This is not a violation of the Liskov substitutability principle, but it could be really confusing for someone trying to use the type. (Depending on whether you called the method via the Map type or the MyHashMap type you could end up using different methods with different semantics ....)
I think it depends a lot on the context and what they exactly asked. For example, is a highly concurrent context, you should have used Hashtable instead. Also, you should specify the types (String, Object) instead of (Object,Object) to get compiler support on the keys.
I think a better design would have been to implement the Map interface and keep the HashMap/HashTable as an internal parameter of the object.

Categories