How to avoid instanceof? [duplicate] - java

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Anything wrong with instanceof checks here?
I have this code
public static int getNumberOfOwned(Player owner, String type){
int count = 0;
for (Field f: board.fieldList)
if (type == "Shipping" && f instanceof Shipping)
if (((Shipping)f).getOwnedBy() == owner)
count++;
else if (type == "Brewery" && f instanceof Brewery)
if (((Brewery)f).getOwnedBy() == owner)
count++;
return count;
}
I don't think this is very elegant and future proof. How can i avoid those instanceof operators.

You can always use an enum, and use a method on Field which returns the type. Here I'll go a little further and surmise that you do not want to update the count for certain types, so the enum is also "adorned" with a boolean expressing that:
enum FieldType {
SHIPPING(true),
BREWERY(true),
NOTME(false);
private final boolean countUpdate;
FieldType(boolean countUpdate) { this.countUpdate = countUpdate; }
public boolean mustUpdateCount() { return countUpdate; }
};
abstract class Field {
protected final FieldType type;
protected Field(FieldType type) { this.type = type; }
public final FieldType getType() { return type; }
public final boolean mustUpdateCount() { return type.mustUpdateCount(); }
}
class Brewery implements Field {
Brewery() {
super(BREWERY);
}
}
and in your code:
FieldType expectedType = Enum.valueOf(type.toUpperCase());
for (Field f: board.fieldlist) {
if (field.getType() != expectedType)
continue;
if (!f.getOwnedBy().equals(owner))
continue;
// Correct type, owned by the correct guy:
// check that we must update; if so, update
if (expectedType.mustUpdateCount())
count++;
}

Since Shipping and Brewery are derived from Field, Field could provide a getter that tells you its type, something like getFieldType(). When Shipping or Brewery are instantiated, they set the appropriate value, or you make getFieldType() abstract and have Shipping and Brewery implement them.

Pass Shipping.class or Brewery.class to your function instead of a string representing the class and check that your Field belongs to that class:
public static int getNumberOfOwned(Player owner, Class<? extends Field> type){
int count = 0;
for (Field f: board.fieldList) {
if (type.isInstance(f) && f.getOwnedBy() == owner) {
count++;
}
}
return count;
}

The best solution would be to use the visitor pattern for this problem, you can look it up here: Visitor pattern
At least it's the object oriented way of handling the problem, but it certainly needs more coding than your current solution.

Add an "isType(String type)" method to Field. For that matter, add "isOwnedBy(String owner)" to Field as well.
for (Field f: board.fieldList)
if (f.isType(type) && f.isOwnedBy(owner))
count++;
return count;

first of all be advice you are using the "==" operator to test the equality of objects, this is most of the the times wrong, and definitely wrong in this case :)
If you want to determine if an object is of an particular type you could use the instanceof operator, see if they have the same class reference (here you could use the "==" operator) or try a cast and check for an exception, the last option is more of a fantasy because in the real world you should not try this.
I am not quite sure what you are trying to do, but you could definitely tailor your objects to avoid the instanceof/class/cast stuff, something like:
class Foxtrot implements Sub {
private SubType type = SubType.ATACK;
public SubType getType() {
return type;
}
}
interface Sub {
enum SubType{ ATACK, BOOMER }
public SubType getType();
}
And you can treat object as implementations of the Sub interface and check with their getType() method what kind they are, it should pretty much work.

You should rewrite this code
1)
class Field
add method getType();
2) code:
public static int getNumberOfOwned(Player owner, String type){
int count = 0;
for (Field f: board.fieldList){
if (f.getType.equals(type)&& f.getOwnedBy().equesl(owner))
count++;
}
return count;
}

Make string getTypeString() a property of Field, and avoid having a bunch of if statements (check type.equals(f.getTypeString())) Make getOwnedBy a property of Field, and don't use a cast. (fields that don't have owners can return null)

Related

Java: alternative to null for special meaning

In Java every variable of a object derived type can be an instance of that type OR null - as far I know.
Is there an alternative? e.g. not an instance but also not null?
I need to represent a special state.
e.g. use it as a parameter in a search-function that can represent a regular value, a null for "empty" or a wildcard for "anything".
Integer n;
n = null; // empty
n = new Integer (11); // regular value
n = ???? // wildcard
search (some_list, n);
The type in the sample is Integer. But it should be generic. So no Integer.MAX would be of help.
I want to do it without any added "flag-variables" - if possible.
There's no way to assign anything that's not either a null value or an instance of the correct type to a reference variable.
But there's ways to simulate that.
Take a look at Optional, it provides an object that can be either absent (very roughly equivalent to null) or present (and have an actual value).
You could do something similar, but with 3 states by creating your own class, let's call it SearchValue:
public class SearchValue<T> {
private final T value;
private final boolean missing;
private final boolean wildcard;
private SearchValue(T value) {
this.value = value;
this.missing = false,
this.wildcard = false;
}
private SearchValue(boolean isMissing) {
this.value = null;
this.missing = isMissing;
this.wildcard = !isMissing;
}
public static <T> SearchValue<T> of(T value) {
return new SearchValue<>(value);
}
public static <T> SearchValue<T> missing() {
return new SearchValue(true);
}
public static <T> SearchValue<T> wildcard() {
return new SearchValue(false);
}
public T getValue() {
if (value == null) {
throw new IllegalStateException("no value specified");
}
return value;
}
public boolean isValue() {
return value != null;
}
public boolean isMissing() {
return missing;
}
public boolean isWildcard() {
return wildcard;
}
}
Any SearchValue instance will return true on exactly one of isValue, isMissing or isWildcard (and only return successfully from getValue() when isValue() returns true).
Note that this can definitely be optimized (by reducing the flags to one field and/or making sure that there's only ever one missing or wildcard instance, since they are interchangable), but the general principle should be clear.
No, a variable is either null, or points to an object of an appropriate type, there is no other possibility. However, you can achieve your goal by encapsulating your search term, e.g.
public class SearchTerm<T> {
private final T value;
public static final SearchTerm WILDCARD = new SearchTerm<Object>(new Object());
public SearchTerm(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
}
To check what type of SearchTerm an instance represents
void doSearch(SearchTerm<String> searchTerm) {
if (searchTerm == SearchTerm.WILDCARD) {
// do a wildcard search
} else if (searchTerm.getValue() == null) {
// do whatever type of search this represents
} else {
// search for items that match this term
String searchTermValue = searchTerm.getValue();
}
}

How to DRY these block of code in Java?

Caller:
switch (type){
case "creature":
Creature returnActor2 = getNextCreature();
boolean isEat2 = actOnNearby(getRightChromosome(Config.HardCode.creature), returnActor2.getLocation());
if (isEat2) {
actOnCreature(returnActor2);
}
break;
case "monster":
Monster returnActor3 = getNextMonster();
boolean isEat3 = actOnNearby(getRightChromosome(Config.HardCode.monster), returnActor3.getLocation());
if (isEat3) {
actOnMonster(returnActor3);
}
break;
}
It will call the following 2 methods:
private Monster getNextMonster() {
ArrayList<Actor> nearbyActors = getActors();
Monster mine = new Monster();
for (Actor a : nearbyActors) {
if (a instanceof Monster) {
mine = (Monster) a;
}
}
return mine;
}
private Creature getNextCreature() {
ArrayList<Actor> nearbyActors = getActors();
Creature mine = new Creature();
for (Actor a : nearbyActors) {
if (a instanceof Creature) {
mine = (Creature) a;
}
}
return mine;
}
The question
As you can see, the getNextXXXXX() method are pretty the same, just return different object, the logic is same, how to DRY? the actOnXXXX() seems falls in the DRY category as well, but it all about the same, use same logic against different object. How to solve this?
Make it accept a classtype:
private <T> T getNext(Class<T> type) {
for (Actor a : getActors()) {
if (type.isAssignableFrom(a.getClass())) {
return (T) a;
}
}
return null; //or type.newInstance(); if you want a guaranteed object, but this restricts your constructor.
}
Or with Java 8:
private <T> T getNext(Class<T> type) {
return (T) getActors().stream()
.filter(a -> type.isAssignableFrom(a.getClass()))
.findFirst().orElse(null);
}
But the usage is the same:
Monster next = getNext(Monster.class);
Breaking down the problem, you know two categories of things:
What you need:
A next object of t type.
A way of determining if an object is t
type
What you have:
The type t you want
A collection of objects, one of which might be t type
A new object via a no-args constructor (or null) if none are there
Additionally, the only variance between all these methods is one thing: Which type it is. So we literally "make that a variable", and as such it becomes a method parameter.
Breaking this down we simply need to organize the code in a manner which accomplishes this:
method: //receives a "type" as a parameter
iterate the list of possible `t`s //our list of objects
if some_t == type //our comparison, previously `a instanceof Type`
return some_t //our result is found
return null //or a new object, but essentially our "default"
The only primary differences here were:
Replacing some_t instanceof Type with type.isAssignableFrom(some_t.getClass())
Reason being here that this is simply how you determine this when using Class<T>
Our default can either be null or a new object
Making the object dynamically via reflection restricts your options and has exceptions to deal with. Returning null or an empty Optional<T> would help signify that you did not have a result, and the caller can act accordingly. You could potentially also just pass the default object itself, and then go back to the instanceof check.
Asking yourself this same hypothesis of "what do I need, and what can I provide/have", will help you map out breaking down the problem into smaller steps, and solving the larger puzzle.
I think, there is a confusion in your code and logic.
FOr example, if you need to iterate on list, you dont need to create a new object. That is, in the following code snippet, "new Monster()" doesn't need to be written
Monster mine = null; // new Monster();
for (Actor a : nearbyActors) {
if (a instanceof Monster) {
mine = (Monster) a;
}
}
Anyway, the answer is the "Type Inference in Java."
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
The answer to your question is
package __TypeInference;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
new Main().doLogic();
}
private void doLogic() {
List<Actor> nearbyActors = getActors();
for (Actor actor : nearbyActors) {
// do with the next actor
System.out.println(actor.toString());
}
}
private List<Actor> getActors() {
List<Actor> actors = new ArrayList<Actor>();
actors.add(new Monster());
actors.add(new Creature());
actors.add(new Monster());
actors.add(new Creature());
return actors;
}
class Monster extends Actor {
#Override
public String toString() {
return "Monster";
}
}
class Creature extends Actor {
#Override
public String toString() {
return "Creatue";
}
}
class Actor {
}
}
I think what you want is to combine getNextMonster and getNextCreature because they have repeated code.
The best thing to do here is to write a generic method that does this:
private <T extends Actor> T getNextActor(T newActor) {
ArrayList<Actor> nearbyActors = getActors();
T mine = newActor;
for (Actor a : nearbyActors) {
if (a instanceof T) {
mine = (T) a;
}
}
return mine;
}
And you can call it like this:
// This is equivalent to calling getNextCreature()
getNextActor(new Creature());
// This is equivalent to calling getNextMonster()
getNextActor(new Monster());
Let me explain the code.
The new method returns a type of Actor. You tell it what kind of actor you want by passing the argument. The argument is necessary because you cannot just initialize a generic type argument like this:
new T();
Because the parameterless constructor might not be available. So that's the job of the caller.
I don't really know what I'm talking about...
This method has the following advantages:
It reduces repeated code
It is flexible - when you want to add another method called getNextXXX (where XXX is a subclass of Actor), you don't need to. Just call getNextActor(new XXX())!
It increases maintainability - if you want to change the implementation of getNextXXX, you can just change one method instead of 2.

Why does Class.isLocalClass always Boolean.FALSE?

I've got a simple Method that returns me always "-undefined-".
public static String getStereoType(Class<?> clazz) {
String result = "-undefined-";
if (clazz.isEnum()) {
result = "enum";
} else if (clazz.isInterface()) {
result = "interface";
} else if (clazz.isLocalClass() || clazz.isMemberClass()) {
result = "class";
}
return result;
}
When I call this Method with Object.class or Long.class always is the result "-undefined-".
List<Class<?>> superClazzes = ClassUtil.getSuperClazzList(clazz);
for (Class<?> c: superClazzess){
String stereoType = ClassUtil.getStereoType(c.getClass());
}
public static List<Class<?>> getSuperClazzList(Class<?> clazz) {
List<Class<?>> resultList = new ArrayList<Class<?>>();
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
resultList.add(superClass);
resultList.addAll(getSuperClazzList(superClass));
}
return resultList;
}
What are you trying to get? So what are the possible stereo types you need? What I know of is:
Enum
Interface
Primitive (e.g. double, but not Double)
Class (everything else!)
And from the class API I can find that you might also differ between:
annotation (actual an interface in java)
synthetic (I'm not sure what this is)
local (defined inside a method)
But they might not be interesting for ULM diagrams at all.
Implementation of toString() from the JVM Class is:
public String toString() {
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName();
}
This might give you some hint as well.
Hope it helps.
Edit: This should do the job:
public static String getStereoType(Class<?> clazz) {
String result = "class";
if (clazz.isEnum()) {
result = "enum";
} else if (clazz.isInterface()) {
result = "interface";
}
else if (clazz.isPrimitive()) {
result = "primitive";
}
return result;
}
To answer the question in your title, isLocalClass() does not always return false: it returns true for types declared within a method. Similarly, isMemberClass() returns true for types declared within another type.
Consider:
public class Outer {
interface MemberClass {}
public static void main(String[] args) {
class LocalClass {}
System.out.printf(
"%s/%s%n",
LocalClass.class.isLocalClass(),
LocalClass.class.isMemberClass()
);
System.out.printf(
"%s/%s%n",
MemberClass.class.isLocalClass(),
MemberClass.class.isMemberClass()
);
}
}
This code, when executed, prints out true/false followed by false/true. Together, they only account for types which are defined within another class, or within a method. Neither strictly depends on the target type being a class as opposed to an interface or enum, so you cannot use it to filter that way.
See #Tarion's excellent answer for the approach you should be taking (and accept his answer, as it more completely solves your problem).

Efficiently access fields by name

A little background as to what I'm trying to achieve:
I'm parsing JSON (over 15GB) and I must store it in memory so any wrappers and extra data is not welcomed, due to the framework and interfaces used within it I must provide functionality to access fields by name. By replacing some String with Enum, Integer with int, Double with double, etc. I'm able to shave about 90% of memory footprint (in comparison with Jackson).
I'm looking to efficiently access the fields at runtime in Java by their name. I'm aware of reflection, but for my case its performance is simply unacceptable, so I don't want to use it.
If it makes the problem easier to solve I'm not too bothered about setting the fields values. I also know at compile time the names of supported fields.
I don't want to store everything in a map i.e. Map<String,Object> due to the memory footprint of boxed object, but I don't mind returning them in a boxed form.
I'm sure this problem was encountered by others and I'm interested in any clever solutions - cleverer than tons of if ... else ... statements.
Let's say the interface to implement is:
public interface Accessor {
Object get(String fieldName);
}
The Object returned by get can be of any type including enum. A naive implementation would be:
public class TestObject implements Accessor {
public enum MyEnum {ONE, TWO, THREE};
private final MyEnum myEnum;
private final int myInt;
private final double myDouble;
private final String myString;
public TestObject(MyEnum myEnum, int myInt, double myDouble, String myString) {
this.myEnum = myEnum;
this.myInt = myInt;
this.myDouble = myDouble;
this.myString = myString;
}
#Override
public Object get(String fieldName) {
if ("myEnum".equals(fieldName)) {
return myEnum;
} else if ("myInt".equals(fieldName)) {
return myInt;
} else if ("myDouble".equals(fieldName)) {
return myDouble;
} else if ("myString".equals(fieldName)) {
return myString;
} else {
throw new UnsupportedOperationException(); // Or could simply return null
}
}
}
What you want is a mapping from a fieldName to a value, the type of which is determined by the fieldName. You know the set of field names up-front, so this is an ideal task for an Enum.
If you don't like the idea of hard-coding each field as an enum, then the variation would be an enum-per-type (MY_FIELD1 becomes MY_ENUM), with a mapping from fieldName to this EnumType.
In the code below I'm making assumptions about the relationship between fieldName and TestObject. Specifically it looks like TestObject is presenting various types of the same value (surely where reasonable), as opposed to a separate value for each field name?
So, to the code:
Rewrite:
#Override
public Object get(String fieldName) {
MyField field = MyField.mapNameToField(fieldName);
if (field == null)
throw new UnsupportedOperationException(); // Or could simply return null
return field.getValue(this);
}
Given (something like):
enum MyField {
MY_FIELD1("myField1") {
public Object getValue(TestObject obj) { return obj.myEnum; }
},
MY_FIELD2("myField2") {
public Object getValue(TestObject obj) { return obj.myInt; }
},
...
;
public abstract Object getValue(TestObject obj);
public String getName() { return name; }
public static MyField mapNameToField(String name) { return map.get(name); }
static {
map = new HashMap<String,MyField>();
for(MyField value: values()) {
map.put(value.getName(), value);
}
}
private MyField(String fieldName) { name = fieldName; }
private String name;
private static Map<String, MyField> map;
}
I've never used this, but looks promising:
http://labs.carrotsearch.com/download/hppc/0.4.1/api/
"High Performance Primitive Collections (HPPC) library provides typical data structures (lists, stacks, maps) template-generated for all Java primitive types (byte, int, etc.) to conserve memory and boost performance."
In particular, the Object{Type}OpenHashMap classes might be what you're looking for:
ObjectByteOpenHashMap
ObjectCharOpenHashMap
ObjectDoubleOpenHashMap
ObjectFloatOpenHashMap
ObjectIntOpenHashMap
ObjectLongOpenHashMap
ObjectShortOpenHashMap
I imagine you would have all 7 of these defined as fields (or whatever subset of them you like), and you would probe each one in turn to see if the key was present for that type of primitive value. E.g.,
if (byteMap.containsKey(key)) {
return byteMap.lget(); // last value saved in a call to containsKey()
} else if (charMap.containsKey(key)) {
return charMap.lget();
} else if {
// and so on...
}
Notice they have their own special lget() method call to optimize the containsKey() / get() usage pattern so typical with maps.

What is the best way to compare several javabean properties?

I need to compare dozens of fields in two objects (instances of the same class), and do some logging and updating in case there are differences. Meta code could look something like this:
if (a.getfield1 != b.getfield1)
log(a.getfield1 is different than b.getfield1)
b.field1 = a.field1
if (a.getfield2!= b.getfield2)
log(a.getfield2 is different than b.getfield2)
b.field2 = a.field2
...
if (a.getfieldn!= b.getfieldn)
log(a.getfieldn is different than b.getfieldn)
b.fieldn = a.fieldn
The code with all the comparisons is very terse, and I would like to somehow make it more compact. It would be nice if I could have a method which would take as a parameter method calls to setter and getter, and call this for all fields, but unfortunately this is not possible with java.
I have come up with three options, each which their own drawbacks.
1. Use reflection API to find out getters and setters
Ugly and could cause run time errors in case names of fields change
2. Change fields to public and manipulate them directly without using getters and setters
Ugly as well and would expose implementation of the class to external world
3. Have the containing class (entity) do the comparison, update changed fields and return log message
Entity should not take part in business logic
All fields are String type, and I can modify code of the class owning the fields if required.
EDIT: There are some fields in the class which must not be compared.
Use Annotations.
If you mark the fields that you need to compare (no matter if they are private, you still don't lose the encapsulation, and then get those fields and compare them. It could be as follows:
In the Class that need to be compared:
#ComparableField
private String field1;
#ComparableField
private String field2;
private String field_nocomparable;
And in the external class:
public <T> void compare(T t, T t2) throws IllegalArgumentException,
IllegalAccessException {
Field[] fields = t.getClass().getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (field.isAnnotationPresent(ComparableField.class)) {
field.setAccessible(true);
if ( (field.get(t)).equals(field.get(t2)) )
System.out.println("equals");
field.setAccessible(false);
}
}
}
}
The code is not tested, but let me know if helps.
The JavaBeans API is intended to help with introspection. It has been around in one form or another since Java version 1.2 and has been pretty usable since version 1.4.
Demo code that compares a list of properties in two beans:
public static void compareBeans(PrintStream log,
Object bean1, Object bean2, String... propertyNames)
throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
Set<String> names = new HashSet<String>(Arrays
.asList(propertyNames));
BeanInfo beanInfo = Introspector.getBeanInfo(bean1
.getClass());
for (PropertyDescriptor prop : beanInfo
.getPropertyDescriptors()) {
if (names.remove(prop.getName())) {
Method getter = prop.getReadMethod();
Object value1 = getter.invoke(bean1);
Object value2 = getter.invoke(bean2);
if (value1 == value2
|| (value1 != null && value1.equals(value2))) {
continue;
}
log.format("%s: %s is different than %s%n", prop
.getName(), "" + value1, "" + value2);
Method setter = prop.getWriteMethod();
setter.invoke(bean2, value2);
}
}
if (names.size() > 0) {
throw new IllegalArgumentException("" + names);
}
}
Sample invocation:
compareBeans(System.out, bean1, bean2, "foo", "bar");
If you go the annotations route, consider dumping reflection and generating the comparison code with a compile-time annotation processor or some other code generator.
I would go for option 1, but I would use getClass().getDeclaredFields() to access the fields instead of using the names.
public void compareAndUpdate(MyClass other) throws IllegalAccessException {
for (Field field : getClass().getDeclaredFields()) {
if (field.getType() == String.class) {
Object thisValue = field.get(this);
Object otherValue = field.get(other);
// if necessary check for null
if (!thisValue.equals(otherValue)) {
log(field.getName() + ": " + thisValue + " <> " + otherValue);
field.set(other, thisValue);
}
}
}
}
There are some restrictions here (if I'm right):
The compare method has to be implemented in the same class (in my opinion it should - regardless of its implementation) not in an external one.
Just the fields from this class are used, not the one's from a superclass.
Handling of IllegalAccessException necessary (I just throw it in the example above).
This is probably not too nice either, but it's far less evil (IMHO) than either of the two alternatives you've proposed.
How about providing a single getter/setter pair that takes a numeric index field and then have getter/setter dereference the index field to the relevant member variable?
i.e.:
public class MyClass {
public void setMember(int index, String value) {
switch (index) {
...
}
}
public String getMember(int index) {
...
}
static public String getMemberName(int index) {
...
}
}
And then in your external class:
public void compareAndUpdate(MyClass a, MyClass b) {
for (int i = 0; i < a.getMemberCount(); ++i) {
String sa = a.getMember();
String sb = b.getMember();
if (!sa.equals(sb)) {
Log.v("compare", a.getMemberName(i));
b.setMember(i, sa);
}
}
}
This at least allows you to keep all of the important logic in the class that's being examined.
While option 1 may be ugly, it will get the job done. Option 2 is even uglier, and opens your code to vulnerabilities you can't imagine. Even if you eventually rule out option 1, I pray you keep your existing code and not go for option 2.
Having said this, you can use reflection to get a list of the field names of the class, if you don't want to pass this as a static list to the method. Assuming you want to compare all fields, you can then dynamically create the comparisons, in a loop.
If this isn't the case, and the strings you compare are only some of the fields, you can examine the fields further and isolate only those that are of type String, and then proceed to compare.
Hope this helps,
Yuval =8-)
since
All fields are String type, and I can modify code of the class owning the fields if required.
you could try this class:
public class BigEntity {
private final Map<String, String> data;
public LongEntity() {
data = new HashMap<String, String>();
}
public String getFIELD1() {
return data.get(FIELD1);
}
public String getFIELD2() {
return data.get(FIELD2);
}
/* blah blah */
public void cloneAndLogDiffs(BigEntity other) {
for (String field : fields) {
String a = this.get(field);
String b = other.get(field);
if (!a.equals(b)) {
System.out.println("diff " + field);
other.set(field, this.get(field));
}
}
}
private String get(String field) {
String value = data.get(field);
if (value == null) {
value = "";
}
return value;
}
private void set(String field, String value) {
data.put(field, value);
}
#Override
public String toString() {
return data.toString();
}
magic code:
private static final String FIELD1 = "field1";
private static final String FIELD2 = "field2";
private static final String FIELD3 = "field3";
private static final String FIELD4 = "field4";
private static final String FIELDN = "fieldN";
private static final List<String> fields;
static {
fields = new LinkedList<String>();
for (Field field : LongEntity.class.getDeclaredFields()) {
if (field.getType() != String.class) {
continue;
}
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
fields.add(field.getName().toLowerCase());
}
}
this class has several advantages:
reflects once, at class loading
it is very simply adding new fields, just add new static field (a better solution here
is using Annotations: in the case you care using reflection works also java 1.4)
you could refactor this class in an abstract class, all derived class just get both
data and cloneAndLogDiffs()
the external interface is typesafe (you could also easily impose immutability)
no setAccessible calls: this method is problematic sometimes
A broad thought:
Create a new class whose object takes the following parameters: the first class to compare, the second class to compare, and a lists of getter & setter method names for the objects, where only methods of interest are included.
You can query with reflection the object's class, and from that its available methods. Assuming each getter method in the parameter list is included in the available methods for the class, you should be able to call the method to get the value for comparison.
Roughly sketched out something like (apologies if it isn't super-perfect... not my primary language):
public class MyComparator
{
//NOTE: Class a is the one that will get the value if different
//NOTE: getters and setters arrays must correspond exactly in this example
public static void CompareMyStuff(Object a, Object b, String[] getters, String[] setters)
{
Class a_class = a.getClass();
Class b_class = b.getClass();
//the GetNamesFrom... static methods are defined elsewhere in this class
String[] a_method_names = GetNamesFromMethods(a_class.getMethods());
String[] b_method_names = GetNamesFromMethods(b_class.getMethods());
String[] a_field_names = GetNamesFromFields(a_class.getFields());
//for relative brevity...
Class[] empty_class_arr = new Class[] {};
Object[] empty_obj_arr = new Object[] {};
for (int i = 0; i < getters.length; i++)
{
String getter_name = getter[i];
String setter_name = setter[i];
//NOTE: the ArrayContainsString static method defined elsewhere...
//ensure all matches up well...
if (ArrayContainsString(a_method_names, getter_name) &&
ArrayContainsString(b_method_names, getter_name) &&
ArrayContainsString(a_field_names, setter_name)
{
//get the values from the getter methods
String val_a = a_class.getMethod(getter_name, empty_class_arr).invoke(a, empty_obj_arr);
String val_b = b_class.getMethod(getter_name, empty_class_arr).invoke(b, empty_obj_arr);
if (val_a != val_b)
{
//LOG HERE
//set the value
a_class.getField(setter_name).set(a, val_b);
}
}
else
{
//do something here - bad names for getters and/or setters
}
}
}
}
You say you presently have getters and setters for all these fields? Okay, then change the underlying data from a bunch of individual fields to an array. Change all the getters and setters to access the array. I'd create constant tags for the indexes rather than using numbers for long-term maintainability. Also create a parallel array of flags indicating which fields should be processed. Then create a generic getter/setter pair that use an index, as well as a getter for the compare flag. Something like this:
public class SomeClass
{
final static int NUM_VALUES=3;
final static int FOO=0, BAR=1, PLUGH=2;
String[] values=new String[NUM_VALUES];
static boolean[] wantCompared={true, false, true};
public String getFoo()
{
return values[FOO];
}
public void setFoo(String foo)
{
values[FOO]=foo;
}
... etc ...
public int getValueCount()
{
return NUM_VALUES;
}
public String getValue(int x)
{
return values[x];
}
public void setValue(int x, String value)
{
values[x]=value;
}
public boolean getWantCompared(int x)
{
return wantCompared[x];
}
}
public class CompareClass
{
public void compare(SomeClass sc1, SomeClass sc2)
{
int z=sc1.getValueCount();
for (int x=0;x<z;++x)
{
if (!sc1.getWantCompared[x])
continue;
String sc1Value=sc1.getValue(x);
String sc2Value=sc2.getValue(x);
if (!sc1Value.equals(sc2Value)
{
writeLog(x, sc1Value, sc2Value);
sc2.setValue(x, sc1Value);
}
}
}
}
I just wrote this off the top of my head, I haven't tested it, so their may be bugs in the code, but I think the concept should work.
As you already have getters and setters, any other code using this class should continue to work unchanged. If there is no other code using this class, then throw away the existing getters and setters and just do everything with the array.
I would also propose a similar solution to the one by Alnitak.
If the fields need to be iterated when comparing, why not dispense with the separate fields, and put the data into an array, a HashMap or something similar that is appropriate.
Then you can access them programmatically, compare them etc. If different fields need to be treated & compared in different ways, you could create approriate helper classes for the values, which implement an interface.
Then you could just do
valueMap.get("myobject").compareAndChange(valueMap.get("myotherobject")
or something along those lines...

Categories