Frage

Ich habe gerade ein Java-n-Tupel, die typsicher ist.
Ich habe einige unkonventionelle Methoden Typsicherheit zu erreichen (Ich habe es nur zum Spaß).

Kann jemand kann etwas Eingang geben es zu verbessern oder einige mögliche Mängel.

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,""),"");
    }
}
War es hilfreich?

Lösung

Kudos auf Learning by Doing. Hier sind Anregungen von „Chancen“ für Verbesserungen:

  1. Nur eine Art von Tuple kann jemals existieren (einmal Typelock Satz ist). Dies schadet Wiederverwertbarkeit und Skalierbarkeit in Programmen mehrere Arten von Tupeln verwenden wollen, es sei denn, Sie greifen Cut-n-Paste Wiederverwendung (BirthdayTuple, DimensionsTuple, StreetAddressTuple, ...). Betrachten wir eine TupleFactory Klasse, die die Zieltypen und erstellt einen Tupel-Builder-Objekt akzeptiert Tupel zu erzeugen.

  2. Die Gültigkeit von „null“ als Wert in einem Tuple ist nicht dokumentiert. Ich denke, bevor Typelock gesetzt ist, null ist erlaubt; aber nach Typelock gesetzt, Code wird eine Nullpointer erzeugen - dies unvereinbar ist. Wenn sie nicht erlaubt sind, sollte der Konstruktor es fangen und nicht zulassen es (unabhängig von Typelock). Wenn sie zulässig sind, dann insgesamt der Code (Konstruktor, gleich, hashcode, usw.) muss Modifikation für sie zu ermöglichen.

  3. Entscheiden Sie, ob Tupeln bestimmt sind unveränderlich Wert Objekte. Basierend auf seinen Mangel an Setter-Methoden, würde ich so denke. Wenn ja, dann vorsichtig sein, „Annahme“, um die eingehenden Array - lastTuple=this.arr. Auch wenn es eine var arg Konstruktor könnte der Konstruktor mit einem Array direkt aufgerufen werden. Die Klasse nimmt das Array (hält einen Verweis darauf) und die Werte in dem Array könnten danach außerhalb der Klasse verändert werden. Ich würde eine flache Kopie des Arrays tun, sondern dokumentiere auch das Potenzial Problem mit Tupeln mit nicht-unveränderlichen Werten (also außerhalb des Tuple geändert werden könnte).

  4. Ihr equals Verfahren fehlt die NULL-Prüfung (if (obj == null) return false) und die Klasse Prüfung (entweder obj instanceof Tuple oder this.getClass().equals(object.getClass())). Die Gleichheits Idiom ist gut dokumentiert.

  5. Es gibt keine Möglichkeit, die Werte eines Tuple außer durch toString anzuzeigen. Dies schützt die Werte und die Gesamt Unveränderlichkeit, aber ich denke, es ist die Nützlichkeit der Klasse begrenzt.

  6. Während ich sich nur ein Beispiel zu realisieren, würde ich nicht erwarten, dass diese Klasse für wie Geburtstage etwas verwenden / Termine. In Lösung Domänen mit festen Objekttypen, real-Klassen (wie Datum) sind so viel besser. Ich würde diese Klasse vorstellt, in bestimmten Bereichen nützlich zu sein, wo Tupel sind Objekte erste Klasse.

Bearbeiten Nachgedacht darüber. Hier ist mein nehmen auf einigen Code (auf 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)

Andere Tipps

Wie ist das typsicher? Sie werfen Laufzeit Ausnahmen statt vom Typ Fehler bei der Kompilierung berichten.

Sie sind zu abstrakt über arity versuchen, die (noch) nicht möglich ist in Sprachen statisch typisierten, ohne Typsicherheit zu verlieren.

Nachtrag:

Tupeln kann von heterogenen Elementen bestehen (d.h. Elemente mit unterschiedlichen Typen). Daher bietet sogar „RUTime Typsicherheit“ nicht möglich, für diese Tuple Klasse. Clients der Klasse sind verantwortlich für die entsprechenden Abgüsse zu machen.

Dies ist das Beste, was Sie in Java tun können: ( Edit: Sehen Brent Post für eine bessere Umsetzung von Tuple. (Es erfordert keine Typumwandlungen auf der Client-Seite.))

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);
  }
}

Dies ist die einfachste Lösung, und es ist auch die beste. Es ist ähnlich, wie Tupeln in .NET vertreten sind. Es ausweicht sorgfältig java Löschen. Es ist stark typisiert. Dabei spielt es keine Ausnahmen auslösen. Es ist sehr einfach zu bedienen.

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;
    }
}

Sie sollten sehen .NET des Tuple Implementierung . Sie sind die Kompilierung typsicher.

Was ist der Zweck des typeLock? Damit jemand verhindern mehr dieser Objekte konstruieren? Dieser Teil macht nicht viel Sinn machen.

Warum sollte man sich nur wünschen jemand verhindern, dass weitere Instantiierung Ihre Objekte lassen? Wenn aus irgendeinem Grunde das ist etwas, was Sie jemals brauchen wird, statt eine Klasse „Sperren“ und Ausnahmen zu werfen, stellen Sie sicher, der Code-Pfad ... nicht mehr Objekte des Typs erstellen.

Was ist der Zweck des statischen lastTuple, die zu einer Referenz der letzten instantiierten Tuple gesetzt? Es ist eine schlechte Praxis statische Referenzen wie diese zu mischen.

Ehrlich gesagt der Code ziemlich verwirrend ist, auch wenn die Notwendigkeit für diese Klasse ist verwirrend. Wenn irgendwie ist dieser Code war ich in einer Arbeitsumgebung bewerten, würde ich es nicht zulassen.

sah diesen Code in Wave-Projekt

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);
  }
}

Hier ist eine wirklich schrecklich n-Tupel Implementierung dass Verwendungen Generika Kompilierung-Typprüfungen zur Verfügung zu stellen. Die wichtigste Methode (zu Demonstrationszwecken zur Verfügung gestellt) zeigen, wie entsetzlich diese nutzen würde:

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
    }
}

Wenn Sie sich in schriftlicher Form typsichere Behälter, Blick in Generika wirklich interessiert:

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);
  }
}

Es wäre besser, Generika zu verwenden, für die Kompilierung Typsicherheit. Sie können eine Schnittstelle pro arity definieren. Dann können Sie separaten aufrufbare Schnittstellen definieren, um die Werte des Tupels zugreifen zu können.

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 ) ; }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top