John
John

Reputation: 45

how to avoid duplicate in the code using generics or Function classes

private <Y> void meth(
            MyObj ds, MultiValueMap<String, List> mvm, Class<Y> data) {

            

        if(data.isAssignableFrom(Employee.class)) {
            for (Employee rd : (List<Employee>) mvm.get(0).get(1)) {
               
                for (String cName : (List<String>) mvm.get(0).get(0)) {
                    ds.setCellValue((String)rd.getDataElement(cName));
                   
                }
               
            }
        }

        if(data.isAssignableFrom(Department.class)) {
            for (Department rd : (List<Department>) mvm.get(0).get(1)) {
               
                for (String cName : (List<String>) mvm.get(0).get(0)) {
                    ds.setCellValue((String)rd.getDataElement(cName));
                    
                }
               
            }
        }
        
        
        //some more similar if conditions as above
        
}

In above, I have like similar 10 if conditions, how to avoid duplicate code in above? Do I need to use any Java 8 Function classes as parameters to avoid duplicate code (or) have to use any extra generics code?

Upvotes: 2

Views: 295

Answers (2)

Kaan
Kaan

Reputation: 5754

While generic methods can be applied, they won't solve the actual scenario you're trying to solve for (an earlier edit of this answer was an attempt before I thought through it more).

Taking a step back, this looks like the problem statement ("what" you're solving for, not "how"):

  • Iterate all lists present in a map – in your posted code, this would be: MultiValueMap<String, List> mvm
  • Accomodate different object types – you posted Employee and Department as examples
  • Whatever the list contains (Employee, Department, etc), you want to call getDataElement()

As the other answer by Ausgefuchster describes, this can be solved with interfaces. I voted that answer up, but wanted to provide more more detail and examples.

Step 1: define an interface

Regardless of concrete class, your map contains lists of things which have getDataElement(), so make an interface which captures that:

interface HasDataElement {
    String getDataElement();
}

Step 2: classes implement the interface

For this answer, I made up a few simple classes – A, B, and C – which implement the interface, but otherwise only return a string when getDataElement() is called. In your code, you would modify Employee, Department, etc. to implement your new interface.

class A implements HasDataElement {
    @Override
    public String getDataElement() {
        return "A";
    }
}

class B implements HasDataElement {
    @Override
    public String getDataElement() {
        return "B";
    }
}

class C implements HasDataElement {
    @Override
    public String getDataElement() {
        return "C";
    }
}

Step 3: handle the map

I'm using built-in types, so Map<String, List> instead of your posted code which uses MultiValueMap<String, List>. I think this difference is not significant, but pointing it out anyway.

The method signature below specifies that the map isn't just <String, List>, but further specifies that the list itself must contain things which extend the HasDataElement interface: List<? extends HasDataElement>.

Inside the method, the same List<? extends HasDataElement> type shows up in the first loop.

Once inside, the concrete class of item isn't relevant – we know it conforms to the HasDataElement interface, so we can call item.getDataElement().

private static void processAllDataElements(Map<String, List<? extends HasDataElement>> map) {
    for (List<? extends HasDataElement> list : map.values()) {
        for (HasDataElement item : list) {
            System.out.println(item.getDataElement());
        }
    }
}

Example usage

Here's a simple example, along with output, that creates a few different lists of the various classes A, B, and C. It then creates a map and adds all three lists.

List<A> listOfA = List.of(new A[]{new A()});
List<B> listOfB = List.of(new B[]{new B(), new B()});
List<C> listOfC = List.of(new C[]{new C(), new C(), new C()});

Map<String, List<? extends HasDataElement>> map = new HashMap<>();
map.put("A", listOfA);
map.put("B", listOfB);
map.put("C", listOfC);

processAllDataElements(map);

A
B
B
C
C
C

Upvotes: 2

Ausgefuchster
Ausgefuchster

Reputation: 1178

So it looks like that what you need is inheritance and not generics. In your if condition you always cast and call the same method on the Object. So what you can do is e.g. define an interface looking something like this:

public interface MyInterface {
    String getDataElement(String name);
}

And implement it in your Employee, Department and other classes you have.


If the method always does the same you can use default or an abstract class to not always write the same:

public interface MyInterface {
    default String getDataElement(String name) {
        //do your thing
        return value;
   }
}
public abstract class MyAbstractClass {
    public String getDataElement(String name) {
        //do your thing
        return value;
   }
}

Now you can change your meth method to this:

private void meth(MyObj ds, MultiValueMap<String, List> mvm) {
    List<MyInterface> list = (List<MyInterface>) mvm.get(0).get(1));

    for (MyInterface rd : list) {
        List<String> cNames = (List<String>) mvm.get(0).get(0);
        for (String cName : cNames) {
            ds.setCellValue((String) rd.getDataElement(cName));
        }
    } 
}

Upvotes: 4

Related Questions