getter / setter的递归函数?

时间:2010-09-04 23:50:00

标签: java arrays

我在Java中寻找一种算法来创建一个对象,该对象将属性设置为对象字符串的第一个非空值。考虑这个数组(为了简单起见,我将使用JSON语法来表示数组):

{
    "objects": [
        {
            "id": 1,
            "val1": null,
            "val2": null,
            "val3": 2.0
        },
        {
            "id": 2,
            "val1": null,
            "val2": 3.8,
            "val3": 6.0
        },
        {
            "id": 3,
            "val1": 1.98,
            "val2": 1.8,
            "val3": 9.0
        }
    ]
}

最后,我想要一个看起来像这样的对象:

{
    "id": 1,
    "val1": 1.98,
    "val2": 3.8,
    "val3": 2.0
}

val1来自第三个对象,val2来自第一个,而val3id来自第一个,因为这些是找到属性的第一个对象't null。

到目前为止,我在Java中所做的是什么工作真的很好:

    // Java code that proceeds a deserialized representation of the 
    // above mentioned array
    int k = 0;
    while (bs.getVal1() == null) {
        k++;
        bs.setVal1(objectArray.get(k).getVal1());
    }

但是,我不满意,因为我必须四次编写此代码(getIdgetVal1getVal2getVal3)。我相信必须有一个相当通用的方法。任何可以给Java初学者提供建议的Java大师吗?

3 个答案:

答案 0 :(得分:2)

在回答实际问题之前,这是编写现有循环的更好方法:(将Object替换为实际类型)

for (Object o : objectArray) {
  Double d = o.getVal1();
  if (d != null) {
    bs.setVal1(d);
    break;
  }
}

考虑到现在布置物体的方式,没有更好的方法。但是如果你改变对象的结构,你可以做得更好。

一种方法是将不同的值字段(val1, val2, ...)放入数组中:

Double val[] = new Double[3]; // for 3 val fields

public Double getVal(int index) {
  return val[index];
}

public void setVal(int index, Double value) {
  val[index] = value;
}

然后,您只需按索引访问字段,并在对象数组的一次迭代中设置bs的所有字段:

for (Object o : objectArray) {
  for (int i = 0; i < 3; i++) {
    Double d = o.getVal(i);
    if (d != null) {
      bs.setVal(i, d);
      break;
    }
  }
}

答案 1 :(得分:1)

+1 - 代码重复是Java中有时难以克服的问题。

一种解决方案是创建一个Iterable类,它允许您迭代其中一个对象中的值,就像它们在数组中一样。这会消除代码中的一些重复,而不会牺牲命名变量的易读性。

请注意,在下面的代码中,我创建了一个单独的可迭代类,但您也可以简单地使POD类可迭代(其中一个是最适合您的选项取决于您未覆盖的详细信息示例代码):

(警告 - 尚未测试)

import java.util.Iterator;


public class Main {

    static class POD{
        Integer id; Double val1; Double val2; Double val3;

        public POD(Integer i, Double v1, Double v2, Double v3){
            id=i; val1=v1; val2=v2; val3=v3;
        }

        public POD(){ }
    }

    static class PODFields implements Iterable<Number>{
        private POD pod;
        public PODFields(POD pod){
            this.pod=pod;
        }
        public PODFieldsIterator iterator() {
            return new PODFieldsIterator(pod);
        }
    }

    static class PODFieldsIterator implements Iterator<Number>{
        int cur=0;
        POD pod;

        public PODFieldsIterator(POD pod) {
            this.pod=pod;
        }

        public boolean hasNext() { return cur<4; }

        public Number next() {
            switch(cur++){
                case 0:
                    return pod.id;
                case 1:
                    return pod.val1;
                case 2:
                    return pod.val2;
                case 3:
                    return pod.val3;
            }
            return null;//(there are better ways to handle this case, but whatever)
        }

        public void remove() { throw new UnsupportedOperationException("You cannot remove a POD field."); }

    }

    public static void main(String[] args) {
        POD [] objectArray = {new POD(1,null,null,2.0),
                             new POD(1,null,null,2.0),
                             new POD(1,null,null,2.0),
                             new POD(1,null,null,2.0)};

        POD finalObject=new POD();

        for (POD cur : objectArray){
            PODFieldsIterator curFields =  new PODFields(cur).iterator();

            for (Number finalValue : new PODFields(finalObject)){
                Number curValue = curFields.next();
                if (finalValue==null)
                    finalValue=curValue;
            }

        }


        for (Number finalValue : new PODFields(finalObject))
            System.out.println(finalValue);
    }
}

编辑:糟糕 - 看起来我忘了数字是不可变的。我想你可以通过迭代器返回函子或其他东西来克服这个问题,但这可能会有点过分。

答案 2 :(得分:1)

每当您想要消除代码重复时,您首先要寻找的是您是否可以提取可重用的方法。反射可以帮助您以可重用的方式调用任意方法。它不是世界上最漂亮的东西,但这种方法适合你:

@SuppressWarnings("unchecked")
public <T> T firstNonNull(String methodName, TestObject... objs) {
    try {
        Method m = TestObject.class.getMethod(methodName, (Class[])null);
        for (TestObject testObj : objs) {
            T retVal = (T)m.invoke(testObj, (Object[])null);
            if (retVal != null) return retVal;
        }
        return null;
    } catch (Exception e) {
        //log, at a minimum
        return null;
    }
}

使用这样的类进行测试:

public class TestObject {
    Integer id;
    String val1;
    Map<String, Boolean> val2;
    public int getId() {
        return id;
    }
    public String getVal1() {
        return val1;
    }
    public Map<String, Boolean> getVal2() {
        return val2;
    }
}

此JUnit测试演示了它的用法:

@org.junit.Test
public void testFirstNonNull() {
    TestObject t1 = new TestObject();
    t1.id = 1;
    t1.val1 = "Hello";
    t1.val2 = null;

    TestObject t2 = new TestObject();
    Map<String, Boolean> map = new HashMap<String, Boolean>();
    t2.id = null;
    t2.val1 = "World";
    t2.val2 = map;

    TestObject result = new TestObject();
    result.id = firstNonNull("getId", t1, t2);
    result.val1 = firstNonNull("getVal1", t1, t2);
    result.val2 = firstNonNull("getVal2", t1, t2);

    Assert.assertEquals(result.id, (Integer)1);
    Assert.assertEquals(result.val1, "Hello");
    Assert.assertSame(result.val2, map);
}