samedi 12 mars 2016

Recursively sum Integers in nested classes with reflection in Java

I've got the following JUnit test:

 @Test
    public void calculateIntegerSum() {
        // given
        ValueSupplier supplier = new ValueSupplier();
        TestClass1 testClass1 = createTestClass1(supplier);
        TestClass2 testClass2 = createTestClass2(supplier);
        TestClass3 testClass3 = createTestClass3(supplier);
        TestClass4 testClass4 = createTestClass4(supplier);
        testClass2.setTestClass1(testClass1);
        testClass2.setTestClass4(testClass4);
        testClass4.setTestClass3(testClass3);
        TestClass2 testClass2a = createTestClass2(supplier);
        testClass3.setTestClass2(testClass2a);
        testClass2a.setTestClass4(createTestClass4(supplier));
        testClass2a.setTestClass1(createTestClass1(supplier));

        // when
        long sum = objectUnderTest.sumIntegers(testClass2);

        // then
        int expectedSum = supplier.integers.stream().mapToInt(value -> value).sum();
        Assertions.assertThat(sum).isEqualTo(expectedSum);
    }

My objectUnderTest is:

private SumObjectGraph objectUnderTest = new SumObjectGraph();

Here is my ValueSupplier:

private static class ValueSupplier {

        Random random = new Random();
        List<Integer> integers = new ArrayList<>();
        List<Float> floats = new ArrayList<>();
        List<String> strings = new ArrayList<>();

        int nextInt() {
            int nextInt = random.nextInt(100);
            integers.add(nextInt);
            return nextInt;
        }

        float nextFloat() {
            float nextFloat = random.nextFloat();
            floats.add(nextFloat);
            return nextFloat;
        }

        String nextString() {
            String nextString = UUID.randomUUID().toString();
            strings.add(nextString);
            return nextString;
        }
    }

And my TestClasses:

private TestClass1 createTestClass1(ValueSupplier supplier) {
        TestClass1 testClass1 = new TestClass1();
        testClass1.setField1(supplier.nextInt());
        testClass1.setField2(supplier.nextInt());
        testClass1.setField3((long) supplier.nextInt());
        testClass1.setField4(supplier.nextString());
        testClass1.setField5((double) supplier.nextFloat());
        testClass1.setField6(supplier.nextFloat());
        testClass1.setField7(new StringBuilder(supplier.nextString()));
        testClass1.setField8(supplier.nextInt());
        return testClass1;
    }

    private TestClass2 createTestClass2(ValueSupplier supplier) {
        TestClass2 testClass2 = new TestClass2();
        testClass2.setField1(new StringBuilder(supplier.nextString()));
        testClass2.setField2(supplier.nextInt());
        testClass2.setField3((long) supplier.nextInt());
        testClass2.setField4(supplier.nextString());
        testClass2.setField5((double) supplier.nextFloat());
        testClass2.setField6(supplier.nextFloat());
        return testClass2;
    }

    private TestClass3 createTestClass3(ValueSupplier supplier) {
        TestClass3 testClass3 = new TestClass3();
        testClass3.setField1(supplier.nextInt());
        testClass3.setField2(supplier.nextInt());
        testClass3.setField3((long) supplier.nextInt());
        testClass3.setField4(supplier.nextString());
        testClass3.setField5(new StringBuilder(supplier.nextString()));
        testClass3.setField6(supplier.nextInt());
        return testClass3;
    }

    private TestClass4 createTestClass4(ValueSupplier supplier) {
        TestClass4 testClass4 = new TestClass4();
        testClass4.setField1(supplier.nextInt());
        testClass4.setField2(supplier.nextInt());
        testClass4.setField3(new StringBuilder(supplier.nextString()));
        testClass4.setField4(supplier.nextInt());
        testClass4.setField5((double) supplier.nextFloat());
        testClass4.setField6(supplier.nextFloat());
        return testClass4;
    }

In order to fulfill this test, I need to implement my SumObjectGraph sumIntegers() method. To do it, i need to use reflection, to see if the current attribute is Long/Integer and sum them up.

The problem I'm getting is that an attribute, could be another class.

So I Thought of using recusivity, to check if the attribute is another class, then loop through its fields.

I got the following attempt:

public long sumIntegers(Object root) {
    return recursivelySum(root, 0L);
}


public static Long recursivelySum(Object ob, Long sum){
    for(Field attribute : ob.getClass().getDeclaredFields()){
        if(TestClass1.class.isAssignableFrom(attribute.getType()) ||
                TestClass2.class.isAssignableFrom(attribute.getType()) ||
                    TestClass3.class.isAssignableFrom(attribute.getType()) ||
                        TestClass4.class.isAssignableFrom(attribute.getType())){
            return recursivelySum(getFields(attribute), sum);
        } else {
            sum += verifyAndSum(attribute, ob);
        }
    }
   return sum;
}

public static Field [] getFields(Object ob) {
    return ob.getClass().getDeclaredFields();
}

public static Long verifyAndSum(Field attribute, Object ob){
    Number tmp = 0;
    Long value = 0L;
    if(long.class.isAssignableFrom(attribute.getType()) ||
            int.class.isAssignableFrom(attribute.getType()) ||
                Long.class.isAssignableFrom(attribute.getType()) ||
                    Integer.class.isAssignableFrom(attribute.getType())){
        try {
            attribute.setAccessible(true);
            tmp = (Number) attribute.get(ob);
            value += tmp.longValue();
        } catch (IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    return value;
}

But it's not quite working. The sum is not what excpected.





Aucun commentaire:

Enregistrer un commentaire