web-dev-qa-db-ja.com

Java N-Tupleの実装

タイプセーフなJava n-Tupleを作成しました。
私は型安全性を達成するためにいくつかの型破りな方法を使用しています(私はただ楽しみのためにそれを作りました)。

誰かがそれを改善することやいくつかの考えられる欠陥についていくつかの意見を与えることができますか?.

public class Tuple {
    private Object[] arr;
    private int size;
    private static boolean TypeLock = false;
    private static Object[] lastTuple = {1,1,1}; //default Tuple type

    private Tuple(Object ... c) {
        // TODO Auto-generated constructor stub
        size=c.length;
        arr=c;
        if(TypeLock)
        {
            if(c.length == lastTuple.length)
                for(int i = 0; i<c.length; i++)
                {
                    if(c[i].getClass() == lastTuple[i].getClass())
                        continue;
                    else
                        throw new RuntimeException("Type Locked");
                }
            else
                throw new RuntimeException("Type Locked");
        }

        lastTuple = this.arr;
    }

    public static void setTypeLock(boolean typeLock) {
        TypeLock = typeLock;
    }

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        if (this == obj)
            return true;

        Tuple p = (Tuple)obj;

        for (int i = 0; i < size; i++)
        {
            if (p.arr[i].getClass() == this.arr[i].getClass())
            {
                if (!this.arr[i].equals(p.arr[i]))
                    return false;
            }
            else
                return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        int res = 17;
        for(int i = 0; i < size; i++)
            res = res*37+arr[i].hashCode();

        return res;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return Arrays.toString(arr);
    }

    public static void main(String[] args) {
        HashMap<Tuple,String> birthDay = new HashMap<Tuple,String>();
        Tuple p = new Tuple(1,2,1986);
        Tuple.setTypeLock(true);
        Tuple p2 = new Tuple(2,10,2009);
        Tuple p3 = new Tuple(1,2,2010);
        Tuple p4 = new Tuple(1,2,2010);
        birthDay.put(p,"Kevin");
        birthDay.put(p2,"Smith");
        birthDay.put(p3,"Sam");
        birthDay.put(p4, "Jack");
        System.out.println(birthDay);
        System.out.println(birthDay.get(new Tuple(1,2,1986)));
        birthDay.put(new Tuple(1,2,""),"");
    }
}
24
Emil

やって学ぶことへの称賛。改善のための「機会」の提案は次のとおりです。

  1. タプルの種類は1つだけです(Typelockが設定されると)。これは、カットアンドペーストの再利用(BirthdayTuple、DimensionsTuple、StreetAddressTupleなど)に頼らない限り、複数のタイプのタプルを使用したいプログラムの再利用性とスケーラビリティを損ないます。ターゲットタイプを受け入れ、タプルを生成するためのタプルビルダーオブジェクトを作成するTupleFactoryクラスについて考えてみます。

  2. タプルの値としての「null」の有効性は文書化されていません。 Typelockが設定される前は、nullが許可されていると思います。ただし、Typelockが設定された後、コードはNullPointerExceptionを生成します-これは一貫性がありません。それらが許可されていない場合、コンストラクターはそれをキャッチして許可しない必要があります(Typelockに関係なく)。それらが許可されている場合は、コード全体(コンストラクター、equals、ハッシュコードなど)を変更して許可する必要があります。

  3. タプルが不変の値オブジェクトであることが意図されているかどうかを決定します。セッターメソッドがないことから、そうだと思います。その場合は、着信配列を「採用」することに注意してください-_lastTuple=this.arr_。 var argコンストラクターですが、コンストラクターは配列を使用して直接呼び出すことができます。クラスは配列を採用し(それへの参照を保持します)、配列内の値は後でクラスの外部で変更される可能性があります。配列の浅いコピーを作成しますが、不変でない値(タプルの外部で変更される可能性がある)を持つタプルの潜在的な問題も文書化します。

  4. equalsメソッドにはnullチェック(if (obj == null) return false)とクラスチェック(_obj instanceof Tuple_またはthis.getClass().equals(object.getClass()))がありません。イコールイディオムは十分に文書化されています。

  5. toStringを使用する以外に、タプルの値を表示する方法はありません。これにより、の値と全体的な不変性が保護されますが、クラスの有用性が制限されると思います。

  6. ほんの一例だとは思いますが、誕生日や日付などにこのクラスを使用することは期待していません。オブジェクトタイプが固定されているソリューションドメインでは、実際のクラス(Dateなど)の方がはるかに優れています。このクラスは、タプルがファーストクラスのオブジェクトである特定のドメインで役立つと思います。

編集これについて考えていました。これがいくつかのコードに関する私の見解です( github + tests ):

_===
Tuple.Java
===
package com.stackoverflow.Tuple;

/**
 * Tuple are immutable objects.  Tuples should contain only immutable objects or
 * objects that won't be modified while part of a Tuple.
 */
public interface Tuple {

    public TupleType getType();
    public int size();
    public <T> T getNthValue(int i);

}


===
TupleType.Java
===
package com.stackoverflow.Tuple;

/**
 * Represents a type of Tuple.  Used to define a type of Tuple and then
 * create tuples of that type.
 */
public interface TupleType {

    public int size();

    public Class<?> getNthType(int i);

    /**
     * Tuple are immutable objects.  Tuples should contain only immutable objects or
     * objects that won't be modified while part of a Tuple.
     *
     * @param values
     * @return Tuple with the given values
     * @throws IllegalArgumentException if the wrong # of arguments or incompatible Tuple values are provided
     */
    public Tuple createTuple(Object... values);

    public class DefaultFactory {
        public static TupleType create(final Class<?>... types) {
            return new TupleTypeImpl(types);
        }
    }

}


===
TupleImpl.Java (not visible outside package)
===
package com.stackoverflow.Tuple;

import Java.util.Arrays;

class TupleImpl implements Tuple {

    private final TupleType type;
    private final Object[] values;

    TupleImpl(TupleType type, Object[] values) {
        this.type = type;
        if (values == null || values.length == 0) {
            this.values = new Object[0];
        } else {
            this.values = new Object[values.length];
            System.arraycopy(values, 0, this.values, 0, values.length);
        }
    }

    @Override
    public TupleType getType() {
        return type;
    }

    @Override
    public int size() {
        return values.length;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getNthValue(int i) {
        return (T) values[i];
    }

    @Override
    public boolean equals(Object object) {
        if (object == null)   return false;
        if (this == object)   return true;

        if (! (object instanceof Tuple))   return false;

        final Tuple other = (Tuple) object;
        if (other.size() != size())   return false;

        final int size = size();
        for (int i = 0; i < size; i++) {
            final Object thisNthValue = getNthValue(i);
            final Object otherNthValue = other.getNthValue(i);
            if ((thisNthValue == null && otherNthValue != null) ||
                    (thisNthValue != null && ! thisNthValue.equals(otherNthValue))) {
                return false;
            }
        }

        return true;
    }

    @Override
    public int hashCode() {
        int hash = 17;
        for (Object value : values) {
            if (value != null) {
                hash = hash * 37 + value.hashCode();
            }
        }
        return hash;
    }

    @Override
    public String toString() {
        return Arrays.toString(values);
    }
}


===
TupleTypeImpl.Java (not visible outside package)
===
package com.stackoverflow.Tuple;

class TupleTypeImpl implements TupleType {

    final Class<?>[] types;

    TupleTypeImpl(Class<?>[] types) {
        this.types = (types != null ? types : new Class<?>[0]);
    }

    public int size() {
        return types.length;
    }

    //WRONG
    //public <T> Class<T> getNthType(int i)

    //RIGHT - thanks Emil
    public Class<?> getNthType(int i) {
        return types[i];
    }

    public Tuple createTuple(Object... values) {
        if ((values == null && types.length == 0) ||
                (values != null && values.length != types.length)) {
            throw new IllegalArgumentException(
                    "Expected "+types.length+" values, not "+
                    (values == null ? "(null)" : values.length) + " values");
        }

        if (values != null) {
            for (int i = 0; i < types.length; i++) {
                final Class<?> nthType = types[i];
                final Object nthValue = values[i];
                if (nthValue != null && ! nthType.isAssignableFrom(nthValue.getClass())) {
                    throw new IllegalArgumentException(
                            "Expected value #"+i+" ('"+
                            nthValue+"') of new Tuple to be "+
                            nthType+", not " +
                            (nthValue != null ? nthValue.getClass() : "(null type)"));
                }
            }
        }

        return new TupleImpl(this, values);
    }
}


===
TupleExample.Java
===
package com.stackoverflow.tupleexample;

import com.stackoverflow.Tuple.Tuple;
import com.stackoverflow.Tuple.TupleType;

public class TupleExample {

    public static void main(String[] args) {

        // This code probably should be part of a suite of unit tests
        // instead of part of this a sample program

        final TupleType tripletTupleType =
            TupleType.DefaultFactory.create(
                    Number.class,
                    String.class,
                    Character.class);

        final Tuple t1 = tripletTupleType.createTuple(1, "one", 'a');
        final Tuple t2 = tripletTupleType.createTuple(2l, "two", 'b');
        final Tuple t3 = tripletTupleType.createTuple(3f, "three", 'c');
        final Tuple tnull = tripletTupleType.createTuple(null, "(null)", null);
        System.out.println("t1 = " + t1);
        System.out.println("t2 = " + t2);
        System.out.println("t3 = " + t3);
        System.out.println("tnull = " + tnull);

        final TupleType emptyTupleType =
            TupleType.DefaultFactory.create();

        final Tuple tempty = emptyTupleType.createTuple();
        System.out.println("\ntempty = " + tempty);

        // Should cause an error
        System.out.println("\nCreating Tuple with wrong types: ");
        try {
            final Tuple terror = tripletTupleType.createTuple(1, 2, 3);
            System.out.println("Creating this Tuple should have failed: "+terror);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace(System.out);
        }

        // Should cause an error
        System.out.println("\nCreating Tuple with wrong # of arguments: ");
        try {
            final Tuple terror = emptyTupleType.createTuple(1);
            System.out.println("Creating this Tuple should have failed: "+terror);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace(System.out);
        }

        // Should cause an error
        System.out.println("\nGetting value as wrong type: ");
        try {
            final Tuple t9 = tripletTupleType.createTuple(9, "nine", 'i');
            final String verror = t9.getNthValue(0);
            System.out.println("Getting this value should have failed: "+verror);
        } catch (ClassCastException ex) {
            ex.printStackTrace(System.out);
        }

    }

}

===
Sample Run
===
t1 = [1, one, a]
t2 = [2, two, b]
t3 = [3.0, three, c]
tnull = [null, (null), null]

tempty = []

Creating Tuple with wrong types: 
Java.lang.IllegalArgumentException: Expected value #1 ('2') of new Tuple to be class Java.lang.String, not class Java.lang.Integer
    at com.stackoverflow.Tuple.TupleTypeImpl.createTuple(TupleTypeImpl.Java:32)
    at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.Java:37)

Creating Tuple with wrong # of arguments: 
Java.lang.IllegalArgumentException: Expected 0 values, not 1 values
    at com.stackoverflow.Tuple.TupleTypeImpl.createTuple(TupleTypeImpl.Java:22)
    at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.Java:46)

Getting value as wrong type: 
Java.lang.ClassCastException: Java.lang.Integer cannot be cast to Java.lang.String
    at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.Java:58)
_
47
Bert F

これはどのようにタイプセーフですか?コンパイル時にタイプエラーを報告する代わりに、ランタイム例外をスローしています。

あなたは、型安全性を失うことなく、静的に型付けされた言語では(今のところ)不可能であるアリティを抽象化しようとしています。

補遺:

タプルは、異種の要素(つまり、異なるタイプの要素)で構成できます。したがって、このTupleクラスでは、「ru​​timetypesafety」でさえ提供することはできません。クラスのクライアントは、適切なキャストを行う責任があります。

これは、Java:編集:Tupleのより良い実装については、 ブレントの投稿 を参照してください。(タイプキャストは必要ありません。クライアント側で。))

final class Tuple {
  private final List<Object> elements;

  public Tuple(final Object ... elements) {
    this.elements = Arrays.asList(elements);
  }

  @Override
  public String toString() {
    return elements.toString();
  }

  //
  // Override 'equals' and 'hashcode' here
  //

  public Object at(final int index) {
    return elements.get(index);
  }
}
11
missingfaktor

これは最も簡単な解決策であり、最高でもあります。これは、タプルが.NETで表される方法と似ています。 Java消去を慎重に回避します。強く型付けされています。例外をスローしません。非常に使いやすいです。

public interface Tuple
{
    int size();
}

public class Tuple2<T1,T2> implements Tuple
{
    public final T1 item1;
    public final T2 item2;

    public Tuple2(
        final T1 item_1,
        final T2 item_2)
    {
        item1 = item_1;
        item2 = item_2;
    }

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

public class Tuple3<T1,T2,T3> implements Tuple
{
    public final T1 item1;
    public final T2 item2;
    public final T3 item3;

    public Tuple3(
        final T1 item_1,
        final T2 item_2,
        final T3 item_3)
    {
        item1 = item_1;
        item2 = item_2;
        item3 = item_3;
    }

    @Override
    public int size()
    {
        return 3;
    }
}
4
intrepidis

。NETのタプルの実装 を確認する必要があります。それらはコンパイル時にタイプセーフです。

3

これは、ジェネリックスを使用してコンパイル時の型チェックを提供する、本当にひどいn-Tuple実装です。 mainメソッド(デモ目的で提供)は、これを使用することがどれほど恐ろしいかを示しています。

interface ITuple { }

/**
 * Typed immutable arbitrary-length tuples implemented as a linked list.
 *
 * @param <A> Type of the first element of the Tuple
 * @param <D> Type of the rest of the Tuple
 */
public class Tuple<A, D extends ITuple> implements ITuple {

    /** Final element of a Tuple, or the single no-element Tuple. */
    public static final TupleVoid END = new TupleVoid();

    /** First element of Tuple. */
    public final A car;
    /** Remainder of Tuple. */
    public final D cdr;

    public Tuple(A car, D cdr) {
        this.car = car;
        this.cdr = cdr;
    }

    private static class TupleVoid implements ITuple { private TupleVoid() {} }

    // Demo time!
    public static void main(String[] args) {
        Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>> triple =
                new Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>>("one",
                        new Tuple<Integer, Tuple<String, TupleVoid>>(2,
                                new Tuple<String, TupleVoid>("three",
                                        END)));
        System.out.println(triple.car + "/" + triple.cdr.car + "/" + triple.cdr.cdr.car);
        //: one/2/three
    }
}

ウェーブプロジェクトでこのコードを見た

public class Tuple<A> {

  private final A[] elements;

  public static <A> Tuple<A> of(A ... elements) {
    return new Tuple<A>(elements);
  }

  public Tuple(A ... elements) {
    this.elements = elements;
  }

  public A get(int index) {
    return elements[index];
  }

  public int size() {
    return elements.length;
  }

  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (o == null || o.getClass() != this.getClass()) {
      return false;
    }

    Tuple<A> o2 = (Tuple<A>) o;
    return Arrays.equals(elements, o2.elements);
  }

  @Override
  public int hashCode() {
    return Arrays.hashCode(elements);
  }

  @Override
  public String toString() {
    return Arrays.toString(elements);
  }
}
1
Parvez Shah

typeLockの目的は何ですか?誰かがこれらのオブジェクトのこれ以上の構築を防ぐことを許可するには?この部分はあまり意味がありません。

なぜあなたは誰かにあなたのオブジェクトのそれ以上のインスタンス化を防がせたいのですか?何らかの理由でこれが必要な場合は、クラスを「ロック」して例外をスローするのではなく、コードパスが...そのタイプのオブジェクトをこれ以上作成しないようにしてください。

最後にインスタンス化されたlastTupleの参照に設定される静的Tupleの目的は何ですか?このような静的参照を混在させることはお勧めできません。

率直に言って、このクラスの必要性は紛らわしいですが、コードはかなり紛らわしいです。どういうわけか、これが私が作業環境でレビューしていたコードである場合、私はそれを許可しません。

1
matt b

コンパイル時の型の安全性のためにジェネリックを使用する方が良いでしょう。アリティごとに1つのインターフェースを定義できます。次に、タプルの値にアクセスするための個別の呼び出し可能インターフェースを定義できます。

interface Tuple1 <T0> { <R> R accept ( Callable1<R,T0> callable ) ; }

interface Tuple2 <T0,T1> { <R> R accept ( Callable2<R,T0,T1> callable ) ; }

...

interface Tuplek <T0,T1,T2,...,Tk> { <R> R accept ( Callablek<R,T0,T1,T2,...,Tk> callable ) ; }

interface Callable1<R,T0> { R call ( T0 t0 ) ; }

interface Callable2<R,T0> { R call ( T0 t0 , T1 t1 ) ; }

....

interface Callablek<R,T0,T1,T2,...,Tk> { R call ( T0 t0 , T1 t1 , T2 t2 , ... , Tk tk ) ; }
0
emory

タイプセーフなコンテナーの作成に本当に興味がある場合は、ジェネリックスを調べてください。

public class Tuple<T> {
  private final T[] arr;
  public Tuple (T... contents) {
    arr = contents;  //not sure if this compiles??
  }

  // etc

  public static final void main(String[] args) {
    Tuple<String> stringTuple = new Tuple<String>("Hello", "World!");
    Tuple<Integer> intTuple = new Tuple<Integer>(2010,9,4);
  }
}
0
romacafe