Try this. The initial reflection is certainly expensive, but if you're going to use it many many times, which I think you will, this is most certainly a better solution what what you're proposing. I don't like using reflection, but I find myself using it when I don't like the alternative to reflection. I do think that this will save your team a lot of headache, but you must pass the name of the method (in lowercase).
In other words, rather than pass "name", you would pass "fullname" because the name of the get method is "getFullName()".
Map<String, Method> methodMapping = null;
public Object getNode(String name) {
Map<String, Method> methods = getMethodMapping(contact.getClass());
return methods.get(name).invoke(contact);
}
public Map<String, Method> getMethodMapping(Class<?> contact) {
if(methodMapping == null) {
Map<String, Method> mapping = new HashMap<String, Method>();
Method[] methods = contact.getDeclaredMethods();
for(Method method : methods) {
if(method.getParameterTypes().length() == 0) {
if(method.getName().startsWith("get")) {
mapping.put(method.getName().substring(3).toLower(), method);
} else if (method.getName().startsWith("is"))) {
mapping.put(method.getName().substring(2).toLower(), method);
}
}
}
methodMapping = mapping;
}
return methodMapping;
}
If you need to access data contained within members of contact, you might consider building a wrapper class for contact which has all methods to access any information required. This would also be useful for guaranteeing that the names of the access fields will always remain the same (I.e. if wrapper class has getFullName() and you call with fullname, it will always work even if contact's getFullName() has been renamed -- it would cause compilation error before it would let you do that).
public class ContactWrapper {
private Contact contact;
public ContactWrapper(Contact contact) {
this.contact = contact;
}
public String getFullName() {
return contact.getFullName();
}
...
}
This solution has saved me several times, namely when I wanted to have a single data representation to use in jsf datatables and when that data needed to be exported into a report using jasper (which doesn't handle complicated object accessors well in my experience).
C# does it because Java did. Java did because Java does not support operator overloading. Since value equality must be redefined for each class, it could not be an operator, but instead had to be a method. IMO this was a poor decision. It is much easier to both write and read a == b
than a.equals(b)
, and much more natural for programmers with C or C++ experience, but a == b
is almost always wrong. Bugs from the use of ==
where .equals
was required have wasted countless thousands of programmer hours.
Best Answer
I guess it's just consistency, or "principle of least astonishment". String is an object, so it would be surprising if was treated differently than other objects.
At the time when Java came out (~1995), merely having something like
String
was total luxury to most programmers who were accustomed to representing strings as null-terminated arrays.String
's behavior is now what it was back then, and that's good; subtly changing the behavior later on could have surprising, undesired effects in working programs.As a side note, you could use
String.intern()
to get a canonical (interned) representation of the string, after which comparisons could be made with==
. Interning takes some time, but after that, comparisons will be really fast.Addition: unlike some answers suggest, it's not about supporting operator overloading. The
+
operator (concatenation) works onString
s even though Java doesn't support operator overloading; it's simply handled as a special case in the compiler, resolving toStringBuilder.append()
. Similarly,==
could have been handled as a special case.Then why astonish with special case
+
but not with==
? Because,+
simply doesn't compile when applied to non-String
objects so that's quickly apparent. The different behavior of==
would be much less apparent and thus much more astonishing when it hits you.