Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics - Type casting issue

I have a generic method which accepts class type and fields of that class to be updated. For ex:

class A {
  private int a;
  private int b;
}

class B {
 private int c;
 private int d;
}

At run time, if we pass class type as "A.class" and fieldstoBeUpdated as "b", what is the best way to access the field's getter/setter of that particular class, so that we can modify the fields.

public <T> void find(T clazz, List<String> fieldsToBeUpdated) {
List<T> collectionList = findAll((Class<T>) clazz);
collectionList.parallelStream().forEach(p -> {
        if (clazz instanceof A) {
            fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
                switch(classFieldName) {
                case "a":((A)p).setA(10);
                break;
                case "b":((A)p).setB(20);
                break;
                }
            });
        }

        if (clazz instanceof B) {
            fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
                switch(classFieldName) {
                case "c":((B)p).setC(30);
                break;
                case "d":((B)p).setD(40);
                break;
                }
            });
        }
    });
}

I have written above code to achieve the same.

But the problem is that i have 30 such classes to be passed as a parameter to this generic method with list of fields of that class to update/modify.

Its not the correct implementation to write 30 such if statements checking for the class type and then type casting the object to that class.

Is there any better way to achieve the same ?

Thanks in advance.

like image 417
Sindhura Gudarada Avatar asked Feb 17 '26 11:02

Sindhura Gudarada


1 Answers

Your A and B classes seem to both provide set/getCreatedTime and set/getUpdatedTime methods.

If the other 28 or so classes provide those as well (as implied by your question), simply have a common interface featuring those methods, for all classes to implement.

Then you can bind the generic type of your method to that interface, and forego all instanceof statements and subsequent explicit casting.

The only drawback would be if you have a field name in your List that does not pertain to the concrete class being passed to the method.

If you want to enforce this, you can use reflection on the object to discover whether the field is present by name. Then, you can easily handle any missing field this with a logged warning (or whichever mechanism you deem appropriated).

Note

As mentioned by thijs-steel, if your "time" methods share the same implementation, you can have your 30 classes extend a common abstract parent that only implements the "time" methods.

Or, you can use default methods since you're clearly using Java 8.

Example

interface I {
    // assuming parameters and return types here
    public void setCreatedTime(ZonedDateTime z);
    public void setUpdatedTime(ZonedDateTime z);
    public ZonedDateTime getCreatedTime();
    public ZonedDateTime getUpdatedTime();
}

// A, B etc. all implement I

public <T extends I> void find(T object, List<String> fieldsToBeUpdated) {
    fieldsToBeUpdated
    .parallelStream()
    .forEach(
        field -> {
            switch(field) {
                case "a": {
                    try {
                        object.getClass().getDeclaredField("a");
                        // we're good
                        object.setCreatedTime(...);
                    }
                    catch (NoSuchFieldException e) {
                        // TODO something
                    }
                    break;
                }
                // ...
            }
        });
}

Update

If your classes share no common fields at all, you may want to change the whole approach altogether.

Instead of having a "one-generic-method-fits-all" logic implementation paradigm, you may want to use inheritance and have your own implementation of the find method in every single class.

That would allow for a smaller switch statement in each implementation, with error handling performed on the default case.

You could also still generalize the behavior by still having a find method taking a T extends Findable (where Findable declares the "time" methods and now the find method), and simply invoke find on the given T object.

Or even separate concerns between Findable and Timed, and have your classes implement both.

like image 158
Mena Avatar answered Feb 20 '26 03:02

Mena