根据索引或者属性的名字从对象中获取对应属性的值

问题描述:

有两种获取对象属性值的方法,第一种是直接获取,比如stu.age; 另一种方法是根据对象属性的名字或者索引获取,比如getValue(stu, “age”);

比如一个类的对象:

class Stu{
    String name;
    int age;
}

如今有一个Stu的对象,并且知道一个属性名“name”,如何获取name所对应的值呢?(这里跟使用student.name来获取是两种不同的意思)。如果要统一定义一个获取属性值的接口, 通过该接口统一获取对象中某一属性的值,那么是:

//obj是对象,fieldName是该对象中某个属性的名字
T getValue(Type obj, String fieldName);

//或者 
//fieldIndex是某一个属性的索引
T getValue(Type obj, int fieldIndex);

问题分析:

平时我们获取一个对象的属性的值是通过直接指定的,代码写好之后固定不变了。比如上面的Stu类,可以直接通过对象获取值,即student.name。但是有时候我们不确定该对象的具体类型,只知道一个对象的引用以及它的field的名字和类型,这样的话是不能这样直接取值的。也就是需要动态取值。

问题解决:

1. 反射

在java中,可以直接使用反射来获取。但是速度有点慢。

2. 统一接口 (Apach Flink的做法)

不使用反射。定义一个抽象类, 方法中提供统一根据索引或者名字获取属性的值的方法。

public abstract class Tuple {

    private static final long serialVersionUID = 1L;

    public abstract <T> T getField(int pos);

    public abstract <T> void setField(T value, int pos);

    /**
     * Gets the number of field in the tuple (the tuple arity).
     *
     * @return The number of fields in the tuple.
     */
    public abstract int getArity();

}

那么如何实现Tuple类呢?如果子类中只有一个属性值,那么实现类为:

public class Tuple1<T0> extends Tuple {

    private static final long serialVersionUID = 1L;

    /** Field 0 of the tuple. */
    public T0 f0;

    /**
     * Creates a new tuple where all fields are null.
     */
    public Tuple1() {}


    public Tuple1(T0 value0) {
        this.f0 = value0;
    }

    @Override
    public int getArity() { return 1; }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getField(int pos) {
        switch(pos) {
            case 0: return (T) this.f0;
            default: throw new IndexOutOfBoundsException(String.valueOf(pos));
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> void setField(T value, int pos) {
        switch(pos) {
            case 0:
                this.f0 = (T0) value;
                break;
            default: throw new IndexOutOfBoundsException(String.valueOf(pos));
        }
    }


    public void setFields(T0 value0) {
        this.f0 = value0;
    }
}

如果子类中只有两个属性值,那么实现类为:

public class Tuple2<T0, T1> extends Tuple {

    private static final long serialVersionUID = 1L;

    /** Field 0 of the tuple. */
    public T0 f0;
    /** Field 1 of the tuple. */
    public T1 f1;

    /**
     * Creates a new tuple where all fields are null.
     */
    public Tuple2() {}

    /**
     * Creates a new tuple and assigns the given values to the tuple's fields.
     *
     * @param value0 The value for field 0
     * @param value1 The value for field 1
     */
    public Tuple2(T0 value0, T1 value1) {
        this.f0 = value0;
        this.f1 = value1;
    }

    @Override
    public int getArity() { return 2; }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getField(int pos) {
        switch(pos) {
            case 0: return (T) this.f0;
            case 1: return (T) this.f1;
            default: throw new IndexOutOfBoundsException(String.valueOf(pos));
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> void setField(T value, int pos) {
        switch(pos) {
            case 0:
                this.f0 = (T0) value;
                break;
            case 1:
                this.f1 = (T1) value;
                break;
            default: throw new IndexOutOfBoundsException(String.valueOf(pos));
        }
    }

    /**
     * Sets new values to all fields of the tuple.
     *
     * @param value0 The value for field 0
     * @param value1 The value for field 1
     */
    public void setFields(T0 value0, T1 value1) {
        this.f0 = value0;
        this.f1 = value1;
    }
}

如果有三个或者更多的属性,实现方法类似。

该方法的速度比反射快很多。基本没有额外的内存占用。所以它算是比较好的方法。但是,需要为每一种数量属性的情况实现一个子类。一般情况先,一个对象的属性数量不会很多。

3. 使用函数指针

该方法可以使用在C++中。因为它不支持反射但支持函数指针。具体为:
定义一个接口,接口的方法和上面一样。但在每一个子类中,增加一个map属性,其key是其他field的名字,value为对应field的getter方法的指针。所有每一个field都应该有getter方法。

扫描二维码关注公众号,回复: 2994745 查看本文章

现在如果知道类的对象和其中的属性名字,可以通过到map中查到getter函数指针,调用该函数获取相应的值。

该方法的缺点显而易见,很麻烦而且占用了很多额外的内存空间。速度方面比反射快。

结束!

猜你喜欢

转载自blog.csdn.net/liangyihuai/article/details/81253027