سؤال

لقد صنعت للتو 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,""),"");
    }
}
هل كانت مفيدة؟

المحلول

مجد على التعلم من خلال القيام به. فيما يلي اقتراحات "الفرص" للتحسين:

  1. يمكن أن يوجد نوع واحد فقط من tuple (بمجرد تعيين typelock). هذا يؤلم قابلية إعادة الاستخدام وقابلية التوسع في البرامج التي ترغب في استخدام أنواع متعددة من tuples إلا إذا كنت تلجأ إلى إعادة استخدام الصغار (عيد ميلاد ، DimensionStuple ، StreetAddressTuple ، ...). فكر في فئة tuplefactory التي تقبل الأنواع المستهدفة وتنشئ كائن منشئ tuple لإنشاء tuples.

  2. لم يتم توثيق صحة "NULL" كقيمة في tuple. أعتقد أنه قبل تعيين Typelock ، يُسمح بالرقم ؛ ولكن بعد تعيين Typelock ، سيقوم الكود بإنشاء nullpointerxception - وهذا غير متسق. إذا لم يُسمح لهم ، يجب على المُنشئ أن يمسك به وعدم السماح به (بغض النظر عن Typelock). إذا تم السماح بها ، فإن الكود بشكل عام (مُنشئ ، يساوي ، هاش كود ، إلخ) يحتاج إلى تعديل للسماح بذلك.

  3. قرر ما إذا كان المقصود أن تكون كائنات قيمة غير قابلة للتغيير. بناءً على افتقارها إلى أساليب Setter ، أعتقد ذلك. إذا كان الأمر كذلك ، فاحرص على "تبني" الصفيف الوارد - lastTuple=this.arr. على الرغم من أنه مُنشئ Var Arg ، يمكن استدعاء المُنشئ مع صفيف مباشرة. يتبنى الفصل الصفيف (يحافظ على الإشارة إليه) ويمكن تغيير القيم الموجودة في الصفيف خارج الفصل بعد ذلك. سأقوم بنسخة ضحلة من المصفوفة ، ولكن أيضًا قم بتوثيق المشكلة المحتملة مع tuples ذات القيم غير القابلة للتطبيق (يمكن تغييرها خارج Tuple).

  4. لك equals الطريقة تفتقر إلى الفحص الفارغ (if (obj == null) return false) وفحص الفصل (إما obj instanceof Tuple أو this.getClass().equals(object.getClass())). تم توثيق المصطلح المتساوي جيدًا.

  5. لا توجد طريقة لعرض قيم tuple إلا من خلال toString. هذا يحمي القيم وعدم قابلية الثبات الكلي ، لكنني أعتقد أنه يحد من فائدة الفصل.

  6. على الرغم من أنني أدرك أنه مجرد مثال ، إلا أنني لا أتوقع استخدام هذا الفصل لشيء مثل أعياد الميلاد/التواريخ. في مجالات الحل مع أنواع الكائنات الثابتة ، تكون الفئات الحقيقية (مثل التاريخ) أفضل بكثير. أتصور أن هذا الفئة مفيدة في مجالات محددة حيث تكون tuples كائنات من الدرجة الأولى.

يحرركنت أفكر في هذا. ها هي رأيي في بعض الكود (على جيثب + الاختبارات):

===
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)

نصائح أخرى

كيف يكون هذا الأنواع ملهمة؟ أنت ترمي استثناءات وقت التشغيل بدلاً من الإبلاغ عن الأخطاء في وقت الترجمة.

أنت تحاول التجريد على Arity وهو (حتى الآن) غير ممكن في اللغات المكتوبة بشكل ثابت ، دون فقدان السلامة.

إضافة:

يمكن أن تتكون tuples من عناصر غير متجانسة (أي عناصر ذات أنواع مختلفة). لذلك فإن توفير حتى "أنواع الأنواع المتقدمة" غير ممكن ، لذلك Tuple صف دراسي. عملاء الفصل مسؤولون عن صنع الممثلين المناسبة.

هذا هو أفضل ما يمكنك القيام به في جافا: (يحرر: نرى منشور برنت لتنفيذ أفضل ل Tuple. (لا يتطلب typecasts على جانب العميل.))

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

هذا هو أبسط الحلول وهو الأفضل أيضًا. إنه مشابه لكيفية تمثيل tuples في .NET. إنه يتجنب بعناية محو جافا. يتم كتابتها بقوة. انها لا ترمي الاستثناءات. إنه سهل الاستخدام للغاية.

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

يجب أن تنظر إلى تنفيذ .NET's Tuple. فهي آمنة للوقت.

ما هو الغرض من typeLock؟ للسماح لشخص ما بمنع بناء المزيد من هذه الأشياء؟ هذا الجزء لا معنى له.

لماذا تريد أن تدع شخصًا ما يمنع المزيد من تثبيت الأشياء الخاصة بك؟ إذا كان هذا شيء تحتاجه لسبب ما ، بدلاً من "قفل" فئة ورمي استثناءات ، فقط تأكد من أن مسار الكود ... لا ينشئ المزيد من الكائنات من النوع.

ما هو الغرض من الثابت lastTuple الذي تم تعيينه على مرجع آخر مثيل له Tuple؟ إنها ممارسة سيئة لخلط المراجع الثابتة مثل هذا.

بصراحة ، فإن الكود مربك للغاية ، على الرغم من أن الحاجة إلى هذه الفئة مربكة. إذا كان هذا بطريقة أو بأخرى رمزًا كنت أراجعه في بيئة عمل ، فلن أسمح بذلك.

شاهد هذا الرمز في مشروع الموجة

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

إليك تطبيق N-Tuple فظيع حقًا يستخدم الأداء العام لتوفير فحوصات نوع الترجمة. تُظهر الطريقة الرئيسية (المنصوص عليها لأغراض تجريبية) مدى رعب ذلك في الاستخدام:

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

سيكون من الأفضل استخدام الأدوية الجيرية لتجميع سلامة نوع الوقت. يمكنك تحديد واجهة واحدة لكل القوس. ثم يمكنك تحديد واجهات منفصلة قابلة للاتصال للوصول إلى قيم tuple.

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 ) ; }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top