java中任意数量组件的元组类

时间:2011-11-17 23:14:18

标签: java

我很好奇如何在java中为任意数量的组件编写一个类?这些组件都是通用类型。是否有语言功能或任何聪明的方法来做到这一点?

谢谢!

4 个答案:

答案 0 :(得分:1)

如果元组的组件都是T的数据类型,那么您只需使用List<T>

如果元组的组件都有不同的数据类型,那么就没有简单的方法了。实际上,Scala(位于JVM之上)通过为n的每个值设置一个单独的类(在引擎盖下)来实现n维元组。例如,以下是二维元组的代码:

public class TwoTuple<T1, T2> {
    private T1 e1;
    private T2 e2;

    public TwoTuple(T1 e1, T2 e2) {
        this.e1 = e1;
        this.e2 = e2;
    }

    public T1 getE1() {
        return this.e1;
    }

    public T2 getE2() {
        return this.e2;
    }
}

您可以类似地实现3元组,4元组等。

不幸的是,如果你想避免强制转换,没有通用的方法来实现它。当然,如果你不介意投射,你可以简单地使用List<Object>

答案 1 :(得分:1)

这是类型安全,因为我可以得到它。我提出的解决方案取决于在“获取”时知道对象的类型:ting或“set”:ting it。如果该类型错误,则抛出一个有意义的运行时异常,解释你做错了什么。

此解决方案在编译期间不是类型安全的。如果你犯了错误,它不会产生编译器错误。但它在运行时是类型安全的,因为它不允许类型的错误。你在构造函数中声明了元组成员的类型,并且这些类型在对象的整个生命周期内得到了加强。

元组类:

/**
 * A utility class for run time type safe mixed collections.
 * 
 * @author davogotland
 */
public class Tuple {
    private Class<?>[]      m_types;
    private Object[]        m_objects;

    /**
     * Constructor, initializes members.
     * 
     * @param objects
     *          An array of class objects, representing the
     *          objects of this tuple, followed by the
     *          objects of this tuple. The order these
     *          objects are passed to the constructor will
     *          decide what index they will be referred to
     *          with as they are being fetched or updated.
     */
    public Tuple(Object... objects) {
        if(objects.length == 0) {
            m_types = new Class<?>[] {};
            m_objects = new Object[] {};
        } else {
            try {
                m_types = (Class<?>[])objects[0];
            } catch(ClassCastException cce) {
                throw new RuntimeException("the first parameter of Tuplet constructor must be an array of class objects.");
            }

            m_objects = new Object[m_types.length];

            if(objects.length != 1) {
                if(m_types.length != (objects.length - 1)) {
                    throw new RuntimeException("the first parameter of Tuplet constructor must be an array with the same length as the number of following arguments.");
                }

                System.arraycopy(objects, 1, m_objects, 0, m_types.length);

                for(int i = 0; i < m_types.length; i++) {
                    try {
                        m_types[i].cast(m_objects[i]);
                    } catch(ClassCastException cce) {
                        throw new RuntimeException("the class objects of the first parameter to Tuple constructor must match the types of the following parameters. error at parameter " + i + ", type of " + m_objects[i] + " declared as " + m_types[i].getName() + " but was " + m_objects[i].getClass().getName() + ".");
                    }
                }
            }
        }
    }

    /**
     * Gets an element from the tuple.
     * 
     * @param <T>
     *          The type of the element to fetch.
     * @param c
     *          A class object representing the
     *          elements type.
     * @param i
     *          The index of the element to fetch. This
     *          index is decided by the order the elements
     *          are added during construction of the
     *          tuple.
     * @return
     *          The element at the given index, cast to
     *          the given type.
     */
    public <T> T get(Class<T> c, int i) {
        if(c != m_types[i]) {
            throw new RuntimeException("the get method for index " + i + " must return a " + m_types[i].getName() + ". (attempted was " + c.getName() + ")");
        }

        return c.cast(m_objects[i]);
    }

    /**
     * Sets an element in the tuple.
     * 
     * @param i
     *          The index where the object should be set.
     * @param object
     *          The object to set.
     */
    public void set(int i, Object object) {
        if(m_types[i] != object.getClass()) {
            throw new RuntimeException("the set method for index " + i + " must take a " + m_types[i].getName() + ". (attempted was " + object.getClass().getName() + ")");
        }

        m_objects[i] = object;
    }
}

考试班(为了更好的安慰):

/**
 * A representation of a fraction.
 * 
 * @author davogotland
 */
public class Fraction {
    private int     m_numerator;
    private int     m_denominator;

    /**
     * Constructor, initializes members.
     * 
     * @param numerator
     * @param denominator
     */
    public Fraction(int numerator, int denominator) {
        m_numerator = numerator;
        m_denominator = denominator;
    }

    /**
     * Calculates the value of this fraction as a double.
     * 
     * @return
     *          The value of this fraction as a double.
     */
    public double getDoubleValue() {
        return (double)m_numerator / (double)m_denominator;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder       builder = new StringBuilder();

        builder.append(m_numerator);
        builder.append("/");
        builder.append(m_denominator);

        return builder.toString();
    }
}

和预期用途:

/**
 * Proving that the class Tuple works.
 * 
 * @author davogotland
 */
public class Main {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Tuple       t = new Tuple(new Class<?>[] {Float.class, Fraction.class, Integer.class}, 2.2f, new Fraction(1, 3), 4);

        System.out.println("first: " + t.get(Float.class, 0));
        //expected output: first: 2.2
        System.out.println("second: " + t.get(Fraction.class, 1));
        //expected output: second: 1/3
        System.out.println("third: " + t.get(Integer.class, 2));
        //expected output: third: 4

        t.set(0, 3.5f);

        double      sum = t.get(Float.class, 0);

        sum += t.get(Fraction.class, 1).getDoubleValue();
        sum += t.get(Integer.class, 2);

        System.out.println("sum: " + sum);
        //expected output: sum: 7.833333333333334

        t = new Tuple(new Object[] {new Class<?>[] {Float.class, Fraction.class, Integer.class}});
        t.set(0, 3.5f);

        System.out.println("first: " + t.get(Float.class, 0));
        //expected output: first: 3.5

        System.out.println("second: " + t.get(Fraction.class, 1));
        //expected output: second: null
        System.out.println("third: " + t.get(Integer.class, 2));
        //expected output: third: null


        sum = t.get(Float.class, 0);

        sum += t.get(Fraction.class, 1).getDoubleValue();
        //expected null pointer exception on the above line
        sum += t.get(Integer.class, 2);

        System.out.println("sum: " + sum);
    }
}

答案 2 :(得分:0)

元组 - 在数学和计算机科学中,元组是一个有序的元素列表(wikipedia

所以我会使用List<T>

答案 3 :(得分:0)

使用任意数量的组件对元组建模的简单方法是:

  • SomeType[]如果元组的大小没有改变
  • List<SomeType>如果元组的大小必须能够改变,
  • Object[]List<Object>如果元组需要能够保存任意(引用)类型的值(通过强制转换使用运行时类型安全),或
  • Tuple2<T1, T2>Triple3<T1, T2, T3>等...即N的每个值的不同通用类。

在Java中没有通用的方法来使用单个类来建模静态类型安全的一般N元组。

(我认为你确实需要静态类型安全;即你希望元组中每个位置的getter和setter都有适当的静态返回类型,这样你就不需要进行类型转换了。不幸的是,那是这部分很难。)